# FPGA 第六組 Lab5 結報 [文章網址](https://hackmd.io/-VSODjKNQuuPo5jIQL7hPg) ## 組員 | 姓名 | 學號 | |:------:|:---------:| | 劉永勝 | F94089032 | | 蔡宗瑾 | E14083137 | | 李宇洋 | E24099025 | ## Problem1 - Simple Computing System ![](https://hackmd.io/_uploads/Hy6QUHEB2.png) ### 電路設計說明 - 本次實作的目的是要學習如何操作BRAM和DSP,將BRAM讀出的資料送進DSP中進行運算,再將算出的結果存入BRAM的指定地址。 - 透過Controller將PS端傳輸進來的instruction解碼,並傳至BRAM0,BRAM1,以及DSP中進行相對應運作。 ![](https://hackmd.io/_uploads/SyceRPNH2.png) - Spec規定BRAM的data width是32bit,且可存取的範圍只有32*32Kb,因此對於BRAM讀取寫入的address bus寬度只需要5bit。 - BRAM0的B port需將資料送進DSP port A,BRAM1的port B除了需將資料送進DSP port B之外,還須將DSP的運算結果寫入其中,因此假如欲完成 ***BRAM1[3] <= BRAM0[0] * BRAM1[2]*** 運算會需要兩個instruction來完成,一個是對BRAM1的port B進行讀取的指令,此時Execute為0,當DSP運算完後,第二筆指令用來寫入BRAM1的port B,此時Execute為1。 #### BRAM0 - 下表為RAMB36E1針對不同port data width所對應的address bus長度,可以看到port data width為36(32 data + 4 parity)所對應的ADDR Bus只有[14:5]是有效地址。 - ADDR Bus[4:0]可為5'b11111或5'b00000,根據助教解釋,後5bit全為1為省電模式,再者,根據實驗結果,全為1或0並不會影響BRAM存取行為。 ![](https://hackmd.io/_uploads/r1gUCZwEBh.png) ![](https://hackmd.io/_uploads/SyabQOVB3.png) 1. Port A(**可以讀以及寫**)與CPU進行溝通,Port B(**單純讀不能寫**)則是將instruction傳進來的地址(bram0_raddr)讀出資料(**DOBDO**)至DSP的A port。 2. 雖然本次實作沒有要求BRAM0的port A可以讀值,但因為方便偵錯,所以還是將DOADO接到BRAM controller中,使得CPU可以讀取BRAM0的內部資料。 3. 經過實驗得知,需要將DOA以及DOB的output register關掉,DSP的input port才會讀到正確且立即性的值。 4. ADDRARDADDR為從processer進來的地址,因為在PS端與PL端溝通的方式都是透過***XPAR_AXI_BRAM_CTRL_NUM_S_AXI_BASEADDR + 4*i** 這行指令進行,其地址的增長是每四個增加,為byte addressing mode,因此只須取ADDRARDADDR[10:2],而忽略ADDRARDADDR[1:0]。 5. ADDRBWRADDR則是從instruction端進來的地址,是word addressing mode,因此不能省略任何部分,為ADDRBWRADDR[4:0]。 ``` verilog= // Attributes RAMB36E1 #( ... // must turn off output registers for immediate read operation for DSP. .DOA_REG(0), .DOB_REG(0), ... // Initial data setup .INIT_00(256'h00000000_00000531_00000000_00000000_00000000_00001201_00000001_00000023), ... // BRAM mode .RAM_MODE("TDP"), ... ); // Instances RAMB36E1_inst ( /*-------------------------PORT A-------------------------------*/ // Port A Data: 32-bit (each) output: Port A data .DOADO(DOADO), // 32-bit output: A port data/LSB data -> output to CPU .ADDRARDADDR({1'b0, 1'b0, ADDRARDADDR[10:2], 5'b11111}), // 16-bit input: A port address/Read address .ENARDEN(ENARDEN), // 1-bit input: A port enable/Read enable .REGCEAREGCE(1'b1), // 1-bit input: A port register enable/Register enable .WEA(WEA), // 4-bit input: A port write enable // Port A Data: 32-bit (each) input: Port A data .DIADI(DIADI), // 32-bit input: A port data/LSB data /*-------------------------PORT B-------------------------------*/ // Port B Data: 32-bit (each) output: Port B data .DOBDO(DOBDO), // 32-bit output: B port data -> output to DSP port A .ADDRBWRADDR({1'b0, 5'b00000, ADDRBWRADDR[4:0], 5'b11111}), // 16-bit input: B port address/Write address .ENBWREN(1'b1), // 1-bit input: B port enable/Write enable .WEBWE(), // 8-bit input: B port write enable/Write enable ); ``` #### BRAM1 ![](https://hackmd.io/_uploads/r1MvfdNSn.png) 1. Port A(**可以讀以及寫**)與CPU進行溝通。Port B(**可以讀可以寫**),其讀與寫的憑據WEB是來自instruction的execute,讀則是將instruction傳進來的地址(bram1_addr)讀出資料(**DOBDO**)至DSP的B port;寫則是將DSP的P port寫入BRAM1的bram1_addr位置。 2. ADDRARDADDR 與 ADDRBWRADDR的概念和BRAM0雷同。 3. DIBDI 是來自DSP P port的運算結果。 ``` verilog= // Attributes RAMB36E1 #( ... // must turn off output registers for immediate read operation for DSP. .DOA_REG(0), .DOB_REG(0), ... // Initial data setup .INIT_00(256'h00000000_00000531_00000000_00000000_00000000_00001201_00000001_00000023), ... // BRAM mode .RAM_MODE("TDP"), ... ); // Instances RAMB36E1_inst ( /*-------------------------PORT A-------------------------------*/ // Port A Data: 32-bit (each) output: Port A data .DOADO(DOADO), // 32-bit output: A port data/LSB data -> output to CPU .ADDRARDADDR({1'b0, 1'b0, ADDRARDADDR[10:2], 5'b11111}), // 16-bit input: A port address/Read address .ENARDEN(ENARDEN), // 1-bit input: A port enable/Read enable .REGCEAREGCE(1'b1), // 1-bit input: A port register enable/Register enable .WEA(WEA), // 4-bit input: A port write enable // Port A Data: 32-bit (each) input: Port A data .DIADI(DIADI), // 32-bit input: A port data/LSB data /*-------------------------PORT B-------------------------------*/ // Port B Data: 32-bit (each) output: Port B data .DOBDO(DOBDO), // 32-bit output: B port data -> output to DSP port A .ADDRBWRADDR({1'b0, 5'b00000, ADDRBWRADDR[4:0], 5'b11111}), // 16-bit input: B port address/Write address .ENBWREN(1'b1), // 1-bit input: B port enable/Write enable .WEBWE({4'd0, WEB, WEB, WEB, WEB}), // 8-bit input: B port write enable/Write enable .DIBDI(DIBDI), // 32-bit input: B port data/MSB data ); ``` #### DSP - 本次實作沒有用到 MASK,PATTERN 比對和 pre-adder功能,因此相對應的register以及attribute暫不詳細討論,但可以注意到DREG為pre-adder的暫存,設為0。我們也沒有用到cascade的功能。 - Input port A,B以及C均選用一個register,以及DSP的OPMODE,ALUMODE,INMODE都會使用一個register,有選用到的register,在instance中的clk enable都需將其開啟。 - ACASCREG 和 BCASCREG 為A端以及B端cascade 路徑上的輸入register數量,必須跟AREG以及BREG數量相同。 - 依照題目要求,P port的register必須開啟,因此我們一個procedure分成兩個instruction執行,第一個階段DSP算出的結果暫存在register中,第二階段會將計算結果**feedback回DPS,執行加0的加法**,再將P register中的值寫入BRAM1中,若沒有feedback的動作,則DSP又會從input A、B的地方取到錯的值計算。 ``` verilog= DSP48E1 #( // Feature Control Attributes: Data Path Selection .A_INPUT("DIRECT"), // Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port) .B_INPUT("DIRECT"), // Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port) .USE_DPORT("FALSE"), // Select D port usage (TRUE or FALSE) .USE_MULT("MULTIPLY"), // Select multiplier usage ("MULTIPLY", "DYNAMIC", or "NONE") ... // Register Control Attributes: Pipeline Register Configuration .ACASCREG(1), // Number of pipeline stages between A/ACIN and ACOUT (0, 1 or 2) .ADREG(0), // Number of pipeline stages for pre-adder (0 or 1) .ALUMODEREG(1), // Number of pipeline stages for ALUMODE (0 or 1) .AREG(1), // Number of pipeline stages for A (0, 1 or 2) .BCASCREG(1), // Number of pipeline stages between B/BCIN and BCOUT (0, 1 or 2) .BREG(1), // Number of pipeline stages for B (0, 1 or 2) .CARRYINREG(0), // Number of pipeline stages for CARRYIN (0 or 1) .CARRYINSELREG(0), // Number of pipeline stages for CARRYINSEL (0 or 1) .CREG(1), // Number of pipeline stages for C (0 or 1) .DREG(0), // Number of pipeline stages for D (0 or 1) .INMODEREG(1), // Number of pipeline stages for INMODE (0 or 1) .MREG(1), // Number of multiplier pipeline stages (0 or 1) .OPMODEREG(1), // Number of pipeline stages for OPMODE (0 or 1) .PREG(1) // Number of pipeline stages for P (0 or 1) ) DSP48E1_inst( .P(P), // 48-bit output: Primary data output // Cascade: 30-bit (each) input: Cascade Ports ... // Control: 4-bit (each) input: Control Inputs/Status Bits .ALUMODE(ALUMODE), // 4-bit input: ALU control input .CARRYINSEL(0), // 3-bit input: Carry select input .CLK(clk), // 1-bit input: Clock input .INMODE(INMODE), // 5-bit input: INMODE control input .OPMODE(OPMODE), // 7-bit input: Operation mode input // Data: 30-bit (each) input: Data Ports .A(A), // 30-bit input: A data input .B(B), // 18-bit input: B data input .C(48'h0000_0009_5514), // 48-bit input: C data input .CARRYIN(0), // 1-bit input: Carry input signal .D(), // 25-bit input: D data input // Reset/Clock Enable: 1-bit (each) input: Reset/Clock .CEA1(1), // 1-bit input: Clock enable input for 1st stage AREG .CEA2(0), // 1-bit input: Clock enable input for 2nd stage AREG .CEAD(0), // 1-bit input: Clock enable input for ADREG .CEALUMODE(1), // 1-bit input: Clock enable input for ALUMODE ... ); ``` - ALUMODE(4bit)是在決定X,Y以及Z muxes出來output的運算模式。 - OPMODE(7bit)是在決定X,Y,以Z muxes的output。 - INMODE(5bit)的[3:0]是在決定 Dual A,D and pre-adder logics,使得multiplier A port 有不同的輸出。INMODE[4]是在決定multipler B port的輸出。詳情可以參見[DSP48E1 user guide notes](https://hackmd.io/Ao7HJne5Qg2GfZF2kQdLWg)。 - 根據不同的operation可以歸類出以下表格: | Operation | ALUMODE | OPMODE|Z output| |:------:|:---------:|:---------:|:---------:| | A*B = Z+X+Y | 0000 | 000_01_01 |0| | A*B+C = Z+X+Y | 0000 | 011_01_01|C| | C-A*B = Z-(X+Y)| 0011 | 011_01_01 |C| | A*B-C-1 = -Z-1+(X+Y)| 0001 | 011_01_01 |C| #### PS C-code 一個procedure分為兩個階段,第一個是DSP計算階段,第二個是BRAM1寫入階段。在第一階段計算結果存放在PREG中,我們須在第二階段將PREG裡的值拉回X mux輸出並和Y以及Z mux輸出的0相加後,將其結果寫入BRAM1中。 ``` c++= // In order to write into BRAM1 // OPMODE[1:0] = 2'b10 for X mux output P // OPMODE[3:2] = 2'b00 for Y mux output 0 // OPMODE[6:4] = 3'b000 for Z mux output 0 /*--------------BRAM1[3] <= BRAM0[0] * BRAM1[2]-----------------*/ // ------------------DSP computing stage------------------------// printf("---------------------------------\r\n"); printf("BRAM1[3] <= BRAM0[0] * BRAM1[2]\r\n"); // 27623 -> check // E/ ALU/ OP / IN/ B1WR/ B1R / B0R // inst = 0_0000_0000101_10001_00000_00010_00000 inst = 0b00000000010110001000000001000000; Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR, inst); // DSP port A checking ... // DSP port B checking ... // DSP port P checking ... // ------------------Writing BRAM1 stage------------------------// // E/ ALU/ OP / IN/ B1WR/ B1R / B0R // inst = 1_0000_0000010_00000_00011_00000_00000 inst = 0b10000000001000000000110000000000; Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR, inst); // test for BRAM1 WEB ... // test for BRAM1 ADDRBWRADDR ... // test for BRAM! DIBDI ... ``` 以上的程式運行結果如下圖所示: ![](https://hackmd.io/_uploads/ByEzPZHS3.png) 可以看到BRAM1[3]的確被更新成DSP P port的輸出結果。 ![](https://hackmd.io/_uploads/r1AtDWHH3.png) ### Block Design ![](https://hackmd.io/_uploads/Hk5wUf_Hn.png) ## Problem 1. PYNQ-Z2上共有多少個DSP48E1 Slice ? 由 PYNQ-Z2 的[user manual](https://www.mouser.com/datasheet/2/744/pynqz2_user_manual_v1_0-1525725.pdf)(下方截圖)可知DSP slice有220個。 ![](https://hackmd.io/_uploads/r1xTSXSB2.png) ## Reference - [DSP48E1 user guide notes](https://hackmd.io/Ao7HJne5Qg2GfZF2kQdLWg) - [7 Series FPGAs Memory Resources User Guide (UG473) ](https://docs.xilinx.com/v/u/en-US/ug473_7Series_Memory_Resources)-see page 29 to 30 for address bus discreption.