Try   HackMD

FPGA 第六組 Lab4 結報

文章網址

組員

姓名 學號
劉永勝 F94089032
蔡宗瑾 E14083137
李宇洋 E24099025

Problem1 - Block RAM Utilize

電路設計說明

這次實作的概念是,以verilog叫出內建的RAMB36E1 IP。實作 True dual port RAM 則代表支援2 read、2 write、1 read/1 write 3種資料傳輸方式。
呼叫方式如下方code範例,分成attributes的基本設置和port descriptions的腳位定義。之後依序說明設置方式和理由。

RAMB36E1 #( // Available Attributes ) RAMB36E1_inst ( // Port Descriptions );
  1. Available Attributes
    依照題目要求實作true dual port RAM,在.RAM_MODE設置為"TDP",下方分項說明各個設定的意義。

    ​​​​RAMB36E1 #( ​​​​. . . ​​​​.RAM_MODE("TDP"), ​​​​. . . ​​​​)

    (1) Set output register
    在memory data輸出的位置,有一個output register,將此暫存器開啟,暫存從memory讀取出來的數值,保證下一級電路能有一個完整的clk cycle取資料。

    ​​​​RAMB36E1 #( ​​​​. . . ​​​​.DOA_REG(1), ​​​​.DOB_REG(1), ​​​​. . . ​​​​)

    (2) Set initial value in RAM
    依題目要求,每32 bit為一個word,並依照指定位置,設置RAM的初始值。其中每4個offset代表4 byte,亦即1 word。例如: offset=28,則將數值設置在第7個word上。
    Offset = 0 : 0x2597
    Offset = 4 : 0x6425
    Offset = 28 : 0x5071
    Offset = 64 : 0x8CF5

    ​​​​RAMB36E1 #( ​​​​. . . ​​​​// INIT_00 to INIT_7F: Initial contents of the data memory array ​​​​.INIT_00(256'h00005071_00000000_00000000_00000000_00000000_00000000_00006425_00002597), ​​​​.INIT_01(256'h0804020108040201080402010804020108040201080402010804020108040201), ​​​​.INIT_02(256'h08040201_08040201_08040201_08040201_08040201_08040201_08040201_00008CF5), ​​​​. . . ​​​​)

    (3) Define width of datapath
    題目要求為32-bit datapath,但須設置為36-bit。4 bit作為parity bit。此外,因為實作true dual port,A、B兩個port的read、write皆須定義為36-bit。

    ​​​​RAMB36E1 #( ​​​​. . . ​​​​.READ_WIDTH_A(36), // 0-72 ​​​​.READ_WIDTH_B(36), // 0-36 ​​​​.WRITE_WIDTH_A(36), // 0-36 ​​​​.WRITE_WIDTH_B(36), // 0-72 ​​​​. . . ​​​​)
  2. Port Descriptions
    先看到dual port RAM的IO名稱,dual port的關係,A、B兩個port各腳位都是成對的。下方依序說明各腳位的功用。

    ​​​​module bram( ​​​​ input clkA, ​​​​ input clkB, ​​​​ input enA, ​​​​ input enB, ​​​​ input [3:0] wen_A, ​​​​ input [3:0] wen_B, ​​​​ input [31:0] data_in_A, ​​​​ input [31:0] data_in_B, ​​​​ input [14:0] addr_A, ​​​​ input [14:0] addr_B, ​​​​ output [31:0] data_out_A, ​​​​ output [31:0] data_out_B ​​​​);

    (1) Set clk port
    在TDP mode中,A、B兩個port是可以獨立的,實現asynchronous讀寫,因此分別輸入不同的clk。此外,為了能用兩個AXI BRAM controller實現dual port,因此必須定義兩個clk接線。

    ​​​​RAMB36E1_inst ( ​​​​ . . . ​​​​ .CLKARDCLK(clkA), ​​​​ .CLKBWRCLK(clkB), ​​​​ . . . ​​​​);

    (2) Set enable output register
    同前面設定output register,將A、B output register enable 設定為1'b1,表示兩個output register永遠開啟。

    ​​​​RAMB36E1_inst ( ​​​​ . . . ​​​​ .REGCEAREGCE(1'b1), ​​​​ .REGCEB(1'b1), ​​​​ . . . ​​​​);

    (3) Set enable RAM
    RAM的read、write enable由兩個訊號控制,分別為enable和write_enable。並沒有所謂的read_enable訊號。enable表示RAM目前可讀、寫,write_enable為high則表示可寫、low表示可讀。下方表格說明兩訊號關係。

    enable write_enable 說明
    1'b0 1'b0 cannot read and write
    1'b0 1'b1 cannot read and write
    1'b1 1'b0 enable read only
    1'b1 1'b1 enable write only

    在TDP mode中,.ENARDEN()、.ENBWREN()表示enable訊號,.WEA()、.WEBWE()則表示write_enable訊號。

    ​​​​RAMB36E1_inst ( ​​​​ . . . ​​​​ .ENARDEN(enA), ​​​​ .WEA(wen_A), ​​​​ .ENBWREN(enB), ​​​​ .WEBWE({4'd0, wen_B}), ​​​​ . . . ​​​​);

    <特別注意>
    .WEA()、.WEBWE()為4-bit enable而非1-bit,原因是此RAM支援byte-wide write。我們的1 word為32-bit,等於4 byte,若我們今天要一次寫入32-bit則write_enable設定為4'b1111。若我們只想寫入32-bit中的LSB 8-bit,則write_enable設定為4'b0001。

    (4) Set input address
    在TDP mode.ADDRARDADDR()、.ADDRBWRADDR()分別代表port A、B的讀,寫16-bit address。

    • Reason about 16-bit address
      RAMB36E1共有36Kb的memory array儲存空間,其組成方式為32Kb的data memory array以及4Kb的parity memory array,因此實際上能夠儲存資料的空間只有
      32Kb=25+10=215bit
      那麼多,但address腳位卻給到16-bit,MSB的位元(A15 pin)供RAM cascade使用,而這次並沒有實作cascade,因此MSB設置為1'b0。
    • Reason about LSB is 3'd0
      在C program中,我們offset的最小單位為1 byte,即為8-bit。因此設定address最小單位為byte,則address的LSB為3'd0。
    ​​​​RAMB36E1_inst ( ​​​​ . . . ​​​​ .ADDRARDADDR({1'b0,addr_A[11:0],3'd0}), ​​​​ .ADDRBWRADDR({1'b0,addr_B[11:0],3'd0}), ​​​​ . . . ​​​​);

    (5) Set input/output datapath
    RAM input/output data皆為32-bit,而.DOADO()、.DOBDO()為output dual port,.DIADI()、.DIBDI()為input dual port。

    ​​​​RAMB36E1_inst ( ​​​​ . . . ​​​​ .DOADO(data_out_A), ​​​​ .DIADI(data_in_A), ​​​​ .DOBDO(data_out_B), ​​​​ .DIBDI(data_in_B), ​​​​ . . . ​​​​);

Utilization of BRAM

根據Product selection guide,BRAM共有140個可使用,而我們這次dual port BRAM使用了其中的1個BRAM。因此從下方utilization report看到我們BRAM用了1/140 = 0.71%。

Block Design截圖

  • 為了實現 真-dual port BRAM,必須使用兩個AXI BRAM controller,分別連接到的BRAM port A、port B上,才能在C program測試時分別灌資料測試。

  • 在接線時,會發現AXI BRAM controller只有一個BRAM_PORTA的接口,接不上自行設定的verilog template BRAM。無意中發現只要點開AXI BRAM controller最右邊的 + 就會出現controller詳細的接口,再手動連接到verilog template BRAM即可。

Testing

// test initial value & port A read printf("Test initial value & port A read:\r\n"); read = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0); printf("[Port A: R] Offset = 0, Data = %x\r\n", read); read = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4); printf("[Port A: R] Offset = 4, Data = %x\r\n", read); read = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 28); printf("[Port A: R] Offset = 28, Data = %x\r\n", read); read = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 64); printf("[Port A: R] Offset = 64, Data = %x\r\n", read); printf("===================\n"); // test port A write printf("Test port A write:\r\n"); for (i = 0; i < 4; i++) { read = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4*i); printf("[Port A: W] Offset = %3d, Data = %x -> %x\r\n", 4*i, read, a[i]); Xil_Out32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4*i, a[i]); printf("Check: MEM[%x] = %x\r\n", XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4*i, Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4*i)); printf("-------------------\n"); } printf("===================\n"); // test port B R/W printf("Test port B read & write:\r\n"); for (i = 0; i < 20; i++) { read = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4*(i+1)); printf("[Port A: R] Offset = %3d, Data = %x\r\n", 4*(i+1), read); read2 = Xil_In32(XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR + 4*i); printf("[Port B: R] Offset = %3d, Data = %x\r\n", 4*i, read2); printf("[Port B: W] Offset = %3d, Data = %x -> %x\r\n", 4*i, read2, read); Xil_Out32(XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR + 4*i, read); printf("Check: MEM[%x] = %x\r\n", XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR + 4*i, Xil_In32(XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR + 4*i)); printf("-------------------\n"); } printf("===================\n");
  • Port A test:
  • Port B test:

Problem

  • <筆記> Block memory - RAMB36E1 分成parity mem、data mem,分別有16、128個word。每個word為256-bit。總共容量為 (16+128) * 256 = 36864,約為36Kb。
  • <筆記>「mebibyte」是數字資訊中的一個位元組數單位。字首「mebi」等於
    220
    ,1 mebibyte等於1,048,576位元組。
  1. PYNQ-Z2上共有多少容量的Block RAM ?
    根據Product selection guide可得知Total Block RAM共有4.9Mb(Mebibyte)。一個Block RAM的容量有36Kb,又共有140個Block RAM,因此總容量為

    (32data+4parity)1024140=5,160,960(bits)=4.921875(Mb)

  2. 承上題,共有多少個RAMB36E1 ?
    本實作板子為PYNQ-Z2,屬於zynq-7000系列Soc家庭,其processing system(PS)的型號為Z-7020,根據Product selection guide可得知Total Block RAM(#36Kb blocks)有140個。

  3. 若要將RAMB36E1 Configure成36Kb FIFO,該使用什麼Verilog Template ?
    下方為實作32-bitwise的FIFO,將RAMB36E1在TDP mode操作,A port為read、B port為write。其中,以head、tail為FIFO的pointer,當head等於tail時,表示記憶體裡面是空的,則無法read data,write data則沒有考慮overflow,永遠都可以write。BRAM設定說明見程式碼註解 (17個設定說明)。

<特別注意>
程式碼第49行,ren需要加上inverter的邏輯,因為write_enable=0才是read data。

module ( input clk, input rst, input [31:0] data_in, input [3:0] wen, // write enable input ren, // read enable output reg empty, full, // FIFO memory R/W enable output [31:0] data_out ); reg [12:0] head; reg [12:0] tail; // control head and tail always @(posedge clk or posedge rst) begin if (rst) begin head <= 13'd0; tail <= 13'd0; end else begin head <= (wen) ? head + 13'd1 : head; tail <= (ren) ? tail + 13'd1 : tail; end end // FIFO is empty cannot read always @(*) begin empty = !(head[12] ^ tail[12]) && (head[11:0] == tail[11:0]); full = (head[12] ^ tail[12]) && (head[11:0] == tail[11:0]); end RAMB36E1 #( // Available Attributes .RAM_MODE("TDP"), // set as true dual port mode .DOA_REG(1), // set A port output register .READ_WIDTH_A(36), // A port read 32-bit data .WRITE_WIDTH_A(0), // A port cannot write .READ_WIDTH_B(0), // B port cannot read .WRITE_WIDTH_B(36) // B port write 32-bit data ) RAMB36E1_inst ( // Port Descriptions .CLKARDCLK(clk), // A, B synchronize .CLKBWRCLK(clk), // A, B synchronize .REGCEAREGCE(1'b1), // enable A port output register .ENARDEN(!empty), // read enable, cannot read when FIFO is empty .WEA({~ren, ~ren, ~ren, ~ren}), // always not write .ENBWREN(!full), // cannot write when FIFO is full .WEBWE({4'd0, wen}), // write 4 byte at once, write_enable = 0 is read .ADDRARDADDR({1'b0, head[11:0], 3'd0}), // read address .ADDRBWRADDR({1'b0, tail[11:0], 3'd0}), // write address .DOADO(data_out), // 32-bit data read out .DIBDI(data_in), // 32-bit data write in ); endmodule

Reference

[1] Vivado Design Suite 7 Series FPGA and Zynq-7000 SoC Libraries Guide (UG953)文檔搜尋: RAMB36E1
[2] 考古
[3] 腳位說明
[4] 範例code