2022-01-15 Verilog HDL実装でPLからBRAMに書き込み、XSDK実装でPSから値を確認する


## 動作環境

- Windows 10 Pro 
- Vivado v2019.1
- CORA Z7 (Z7-07S)

## 概要

spi ADCにて8MS/sのデータをPLで取込み、そのデータをPSから任意タイミングで読みたい。
そのための予備的な技術情報整理を進めている。

今回はPLでBRAMに書き込み、PSから読み取る実装について整理しておく。

- Verilog HDLでBRAMに書き込む
    - 接続相手はBlock Memory Generatorのブロック
- XSDKでPSからBRAMを読み取る
    - メモリ指定読み出し

## 内容

### Verilog HDLコード

```bramWriter.v
`default_nettype none

/*
N個のデータをBRAMに書き込む

### 動作
リセット直後にN個のデータを書き込む
[SHIFT_VAL]からインクリメントされた値が書かれる
BRAM(Block Memory Generator IP)に接続して使用する
*/

module bramWriter
  (
    input wire clk,
    input wire resetn,
    output reg [31:0] addr_0,
    output reg [31:0] data_0,
    output reg [3:0] we_0 // Write Enable
  );

    localparam NUM_DATA = 128; // 書き込むデータ数
    localparam SHIFT_VAL = 100; // データをこの値からインクリメントする
    
    reg [31:0] counter; // 書き込みアドレス値に使う

    always @(posedge clk) begin
        if(resetn == 0) begin // リセット時の初期化
            counter <= 0;
            we_0 <= 4'h0;
        end else begin // 通常処理 (nominal path)
            if(counter < NUM_DATA) begin
                counter <= counter + 1;
                we_0 <= 4'hF; // Write Enable ON
            end else begin
                we_0 <= 4'h0; // Write Enable OFF
            end

            addr_0 <= {counter[29:0], 2'b00}; // 書き込みアドレス. 2'b00: アドレスを4ずつずらすため
            data_0 <= counter + SHIFT_VAL; // 書き込みデータ
        end
    end

endmodule // bramWriter

`default_nettype wire
```
    
###  テストベンチ

```bramWriter_tb.v
`timescale 1ns / 1ps

module bramWriter_tb();

localparam STEP = 8;
localparam CLKNUM = 800;

// for input 
reg CLK;
reg RSTN;
// for output
wire [31:0] addr0;
wire [31:0] data0;
wire [3:0] we0;

bramWriter bramWriter(
    .clk(CLK),
    .resetn(RSTN),
    .addr_0(addr0),
    .data_0(data0),
    .we_0(we0)
);

always begin
    CLK = 0; #(STEP/2);
    CLK = 1; #(STEP/2);
end

initial begin
    // 初期化
    RSTN = 1;
    #(STEP*5) RSTN = 0;
    #(STEP*5)  RSTN = 1;

    // CLK処理
    #(STEP*CLKNUM);
    $stop;
end

endmodule
```

### Run Simulation結果

Run Simulationで100usまで実行した結果は以下の通り。

<img width="1488" alt="image.png (29.3 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2022/01/15/21741/ebc9da67-9d63-42ed-a315-d5c237c21f21.png">

リセット解除直後の書き込みは以下の通り。

<img width="1279" alt="image.png (26.8 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2022/01/15/21741/5cddacba-972d-4838-9ce2-0d5fe4f4ac3d.png">

アドレス0x00に対して0x64(100)が書き込まれることを確認できた。

### Block DesignとAddress

bramWriter.vを「Add Module to Block Design」してBlock Designに配置した。
Zynqを追加して、PSからBRAM読み取りできるように配置配線した。

<img width="1497" alt="image.png (91.9 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2022/01/15/21741/ecf164f5-f287-49bc-840f-32b3afa4b313.png">

Block Memory Generatorの設定は以下の通り。
<img width="1293" alt="image.png (34.9 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2022/01/15/21741/c7a4beeb-bbe3-446f-aae8-b0b234514092.png">

Address Editorペインは以下の通り。
<img width="808" alt="image.png (11.6 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2022/01/15/21741/b5d1fe95-4eb6-473a-b9e1-25ea1056298b.png">

### XSDK実装

上記にてbitstreamを生成した。
以下のXSDK用コードを用意した。

```xbram_example.c
#include <stdio.h>
#include "xil_printf.h"
#include "xparameters.h"
#include "xbram.h"

#define BRAM_BASE  (XPAR_BRAM_0_BASEADDR)

int main()
{
    printf("Hello 1030\r\n");

    u32 *brams = (u32 *)BRAM_BASE; // 配列アクセス用
    for(int idx = 0; idx < 100; idx++) {
        printf("value in BRAM %ld\r\n", brams[idx]);
    }

    return 0;
}
```

### 実行結果

<img width="670" alt="image.png (17.5 kB)" src="https://img.esa.io/uploads/production/attachments/6206/2022/01/15/21741/8bd18907-6b0a-41a1-b6f7-308884f7f805.png">

値が100からインクリメントされることを確認できた（正常）。


## 関連

- PG058 : Block Memory Generator v8.4
    - https://www.xilinx.com/support/documentation/ip_documentation/blk_mem_gen/v8_4/pg058-blk-mem-gen.pdf