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