片上系统设计思想与源代码分析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.9 WISHBONE对RAM/ROM的支持

3.9.1 WISHBONE与RAM和ROM的互联

遵守WISHBONE规范的IP可以与任何类型的RAM和ROM互联,只是对于不同的RAM和ROM,接口的复杂度不同,工作效率也不同。WISHBONE规范在设计时更多地考虑了对典型RAM和ROM的高效支持,而这些RAM/ROM在FPGA和ASIC片内是普遍使用的。

WISHBONE主设备接口与RAM的连接示例如图3-20所示。典型的同步RAM具有一个时钟输入,一个写使能信号WE,地址信号以及数据输入DIN、数据输出信号DOUT,有时还包括片选信号CE。图3-20中图(a)表示RAM写操作。在时钟上升沿0,主设备将ADR_O()、DAT_O()置为合适的值,将WE_O置高表示写操作;在时钟上升沿1,DAT_O()被写到ADR_O()对应的RAM位置。图3-20中图(b)表示RAM读操作。在时钟上升沿0,主设备将ADR_O()置为合适的值,将WE_O置低表示读操作;在时钟上升沿1,DAT_O()被主设备采样。

图3-20 WISHBONE主设备接口与同步RAM的连接

(b)异步读周期

(a)同步写周期

WISHBONE主设备接口与ROM的连接和时序更加简单,只需要地址输入ADR()和数据输出DOUT(),时钟信号都不需要,如图3-21所示。

图3-21 WISHBONE主设备接口与ROM的连接

3.9.2 WISHBONE兼容的RAM和Flash仿真模型

在OR1200的验证中将使用到WISHBONE兼容的RAM和Flash模型,因此,在这里给出所使用的RAM和Flash的源代码。

RAM的大小为2MB,是实际FPGA或ASIC RAM如idt71256sa15 SRAM的等效行为模型,其源代码如下:

module sram_top (
  wb_clk_i, wb_rst_i,
  wb_dat_i, wb_dat_o, wb_adr_i, wb_sel_i, wb_we_i, wb_cyc_i,
  wb_stb_i, wb_ack_o, wb_err_o,
);
……
parameter       aw = 19;
……
reg    [7:0]         mem [2097151:0];//RAM为2MB,因此地址宽度aw = 19
//地址不能大于2M
assign wb_err_o = wb_cyc_i & wb_stb_i & (|wb_adr_i[23:21]);
assign adr = {8'h00, wb_adr_i[23:2], 2'b00};
//SRAM读逻辑
assign wb_dat_o[7:0] = mem[adr+3];
assign wb_dat_o[15:8] = mem[adr+2];
assign wb_dat_o[23:16] = mem[adr+1];
assign wb_dat_o[31:24] = mem[adr+0];
//写逻辑
always @(posedge wb_rst_i or posedge wb_clk_i)
      if (wb_cyc_i & wb_stb_i & wb_we_i) begin
            if (wb_sel_i[0])
                mem[adr+3] <= #1 wb_dat_i[7:0];
            if (wb_sel_i[1])
                mem[adr+2] <= #1 wb_dat_i[15:8];
            if (wb_sel_i[2])
                mem[adr+1] <= #1 wb_dat_i[23:16];
            if (wb_sel_i[3])
                mem[adr+0] <= #1 wb_dat_i[31:24];
      end
assign wb_ack_o = wb_cyc_i & wb_stb_i & ~wb_err_o;
endmodule

所使用的Flash实际上是一个只读的Flash,模型仿真了典型的NorFlash如INTEL 28f016s3 FLASH的读时序,而忽略了写时序。主设备每读32比特的数据需要4个时钟周期。其源代码如下:

module flash_top (
    wb_clk_i, wb_rst_i,
    wb_dat_i, wb_dat_o, wb_adr_i, wb_sel_i, wb_we_i, wb_cyc_i,
    wb_stb_i, wb_ack_o, wb_err_o
  );
  //内部数据线和寄存器
  reg [7:0]       mem [2097151:0]; //2M字节
  wire    [31:0]      adr;
  reg         wb_err_o;
  reg [31:0]      prev_adr;
  reg [1:0]       delay;
  wire            wb_err;
  //
  assign adr = {8'h00, wb_adr_i[23:2], 2'b00};
  //Flash的初始化
  initial $readmemh("../src/flash.in", mem, 0);
  //读模型,数据被一次赋值
  assign wb_dat_o[7:0] = wb_adr_i[23:0] < 65535 ? mem[adr+3] : 8'h00;
  assign wb_dat_o[15:8] = wb_adr_i[23:0] < 65535 ? mem[adr+2] : 8'h00;
  assign wb_dat_o[23:16] = wb_adr_i[23:0] < 65535 ? mem[adr+1] : 8'h00;
  assign wb_dat_o[31:24] = wb_adr_i[23:0] < 65535 ? mem[adr+0] : 8'h00;
    // delay逻辑,该逻辑的目的是每次发生读操作,都在4个周期后产生应答信号wb_ack_o
    always @(posedge wb_clk_i or posedge wb_rst_i)
        if (wb_rst_i) begin
            delay <= #1 2'd3;
            prev_adr <= #1 32'h0000_0000;
        end
        else if (delay && (wb_adr_i == prev_adr) && wb_cyc_i && wb_stb_i)
            delay <= #1 delay -2'd1;
        else if (wb_ack_o || wb_err_o || (wb_adr_i != prev_adr) || ~wb_stb_i) begin
            delay <= #1 2'd0;   // delay ... can range from 3 to 0
            prev_adr <= #1 wb_adr_i;
        end
    assign wb_ack_o = wb_cyc_i & wb_stb_i & ~wb_err & (delay == 2'd0) & (wb_adr_i == prev_adr);
    //总线操作错误信号,错误产生的原因是输入地址值过大
    assign wb_err = wb_cyc_i & wb_stb_i & (delay == 2'd0) & (|wb_adr_i[23:21]);
    always @(posedge wb_clk_i or posedge wb_rst_i)
        if (wb_rst_i)
            wb_err_o <= #1 1'b0;
        else
            wb_err_o <= #1 wb_err & !wb_err_o;
    endmodule