Rocket-chip-RoCC(2)

Xxxxxxx..發表於2020-12-24

rocket-chip-rocc

關於rocc的內容很多,我會分多章進行講解。
初步規劃以下面章節為劃分。

1. 協議,自定義指令說明。
2. rocket-chip RoCC各模組的scala程式碼說明。
3. 軟體程式碼編譯實現。
4. rocket-chip RoCC各模組硬體實現和硬體模擬。

第二部分rocket-chip RoCC各模組的scala程式碼說明。

1) Rocket-chip中的定製CSR暫存器
利用grep指令搜尋scala中關於0x7c0和0x7c1的內容,結果如下圖。
在這裡插入圖片描述
順著找下去可以找到RocketCore.scala模組。
在這裡插入圖片描述
利用bpmCSR(bpmCSRId=0x7c0)和chickenCSR(chickenCSRId=0x7c1)定義不同的方法。
在這裡插入圖片描述
getOrElse()主要就是防範措施,如果有值,那就可以得到這個值,如果沒有就會得到一個預設值。

def getOrElse[B1 >: B](key: A, default: => B1): B1 = get(key) match {    
case Some(v) => v
    case None => default
}

從API中可以看出,傳入的引數是(key,default)這種形式,返回值是:如果有key那就get(key),如果沒有,就返回default。
在程式碼中的意思是如果chickenCSRId=0x7c1存在相應的bit,則取對應bit的值,如果不存在相應的bit,則取false.B/true.B值,固定相應def的值。
可以通過繼續追查disableICachePrefetch等關鍵字,找到使用這些def的具體位置。
0x7c0和0x7c1的內相關容可以看前文。

2) Rocket-chip中定製指令的使用
我們利用grep和正規表示式可以輕易搜到定製指令相關的scala原始檔,如下圖所示。
在這裡插入圖片描述
可以看到,與rocket-chip定製指令相關的scala檔案有sybsystem/Configs.scala和tile/LazyRoCC.scala。
在sybsystem/Configs.scala中對各定製指令模組進行例化,並指定各模組使用的定製指令編號,具體入下圖。
在這裡插入圖片描述
再利用WithRoccExample作為關鍵字進行搜尋後,可以在system/Configs.scala檔案中找到WithRoccExample的呼叫,使用的方式為:new WithRoccExample ++。這樣編譯後,生成的rocket-chip RTL就會帶有accumulator、translator、counter和blackbox這四個模組。

下面對tile/LazyRoCC.scala的部分程式碼進行簡單說明。
先看一下rocc統一的模組埠,如下圖。
在這裡插入圖片描述
我在第一章節已經說明了,custom0~3都是使用R類指令格式的,所以可以看到上圖中RoCCInstruction就是按照R類指令格式來定義的,funct就是funct7,而xd、xs1和xs2就是funct3。在accumulator模組中xs1和xs2沒有用到,xd是作為需要返回計算結果到rd暫存器的標誌。也就是說,funct、rs2(32個通用暫存器的編號)、rs1(32個通用暫存器的編號)、xd、xs1、xs2、rd(32個通用暫存器的編號)、opcode都要以散開訊號的形式進入到rocc模組中,這裡的rocc模組沒有特定指明是哪個,也就是全部都要遵守這個約定。
繼續看RoCCCommand,首先將剛定義的RoCCInstruction拿出來,然後再加了rs1、rs2和status。這裡的rs1和rs2和前面RoCCInstruction的不同,這裡的rs1和rs2不是32個通用暫存器的編號,是該對應編號通用暫存器所儲存的數值,具體就是0-31共32個通用暫存器,現在的編號2暫存器存的32bits資料為0x12345678,而編號7存的是0xFFFFAAAA,如果RoCCInstruction中的rs1為2,rs2為7,則RoCCCommand中的rs1為0x12345678,rs2為0xFFFFAAAA,它們都是xLen的寬度,我的匯流排寬度為32bits,所以它們的儲存值也就是32bits。status就是CSR暫存器mstatus的值。到這裡,RoCCCommand在RoCCInstruction的基礎上增加了三個訊號,這些在rocc模組中都要宣告這些埠。
接下來是定義RoCCResponse,是rocc模組應答的訊號,rd是通用暫存器的編號,共5bits,data是返回的計算結果,共xLen位。
在這裡插入圖片描述
然後就是RoCCCoreIO,將RoCCCommand以cmd為字首,然後散開為各個訊號,RoCCRespons則以resp為字首,然後多宣告mem、busy、initerrupt、exception這幾個訊號,很好理解,mem就是memory的匯流排介面,HellaCacheIO不是在tile/LazyRoCC.scala定義的,所以要自己去找,這裡就不說了。busy定義為output,interrupt定義為output,exception定義為input,這三個訊號後面可以直接用這樣的名字來使用,沒有那些字首的存在。
最後就是RoCCIO,是在RoCCCoreIO的基礎上增加了ptw、fpu_req和fpu_resp這三組訊號,用組作為單位,是因為它們是捆綁的一組埠訊號。ptw要看nPTWPorts這個引數的輸入,如果nPTWPorts=0,那麼久不存在ptw這組埠了,同理,如果沒有usingFPU(這個引數在其他地方定義的),那麼fpu_req、fpu_resp也會沒有,因為我配置的rocket-chip是不存在浮點運算的,所以usingFPU=0,所以全部rocc模組都沒有生成fpu_req和fpu_resp介面。
做一個小結,如果你配置的rocket-chip是使用了浮點運算,且你定製模組功能中需要使用到ptw的,那麼你的rocc模組介面應該包含RoCCCommand散開、RoCCRespons散開、mem、busy、initerrupt、exception、ptw散開、fpu_req散開和fpu_resp散開這麼多的訊號。

下面看一下accumulator和translator模組RTL的埠。
Accumulator:
在這裡插入圖片描述在這裡插入圖片描述
Translator:
在這裡插入圖片描述
看圖就行了,我就不說明了。
需要注意的是,在scala程式碼中如果RoCCIO中某些訊號你不需要用到,那隻需要將這部分訊號強接false,那麼就會在生成RTL的時候對這些訊號進行優化去掉。也就是說RoCCIO是一個總的類,裡面就申明瞭那麼多的訊號,用不用看你自己的實際情況。

持續ing。