jlon
Newbie level 4
As part of a larger project, I am trying to model a memory subsystem and am having trouble. It's a static async SRAM with an 8-bit bidirectional IO port.
The SRAMs are loading their images from the input files properly. The values when the 'A' (CPU) side is writing are being saved in the SRAM, I think. But when I enable the SRAM for output, it doesn't drive the stored values onto the bus, so the testbench (which simulates a CPU for now) cannot retrieve memory contents. I've stared at this and tried many (wrong) things for a few days now, and I'm stumped.
If I comment out the instantiation of the SRAMs and just drive outputB_r directly, bus_data shows its values. But when I include the SRAMs and don't drive outputB_r with register assignment, I get nothing.
Can anyone set me straight?
Complete code follows:
The SRAMs are loading their images from the input files properly. The values when the 'A' (CPU) side is writing are being saved in the SRAM, I think. But when I enable the SRAM for output, it doesn't drive the stored values onto the bus, so the testbench (which simulates a CPU for now) cannot retrieve memory contents. I've stared at this and tried many (wrong) things for a few days now, and I'm stumped.
If I comment out the instantiation of the SRAMs and just drive outputB_r directly, bus_data shows its values. But when I include the SRAMs and don't drive outputB_r with register assignment, I get nothing.
Can anyone set me straight?
Complete code follows:
Code:
`timescale 1ns/100ps
module sram45C_test;
reg bus_oeA; // CPU-driven bus enable
reg bus_oeB; // CPU-driven bus enable
reg sram_oe_; // CPU-driven SRAM enable
reg [15:0] addr; // CPU-driven address
//-------------------------------------------------------------------------
wire [15:0] outputA;
reg [15:0] outputA_r; // used to drive outputA when A is writing
wire [15:0] outputB;
reg [15:0] outputB_r; // used to drive outputB when B is writing
wire [15:0] bus_data; // data from other devices shows up here
reg [15:0] bus_r; //drives 'bus_data' based on what side is enabled
//-------------------------------------------------------------------------
// create a bidirectional bus; either 'A' or 'B' drives
always @ (outputA or outputB or bus_oeA or bus_oeB)
begin
case ({bus_oeB, bus_oeA})
{1'b0, 1'b1}: bus_r = outputA;
{1'b1, 1'b0}: bus_r = outputB;
default: bus_r = 16'bz;
endcase
end
assign bus_data = (bus_oeA | bus_oeB) ? bus_r : 16'bz;
//-------------------------------------------------------------------------
// create two SRAMS
sram45C_mod #(.BANK(0)) sram0 (
.addr(addr), .data(bus_data[7:0]),
.ce1_(1'b0), .ce2(1'b1), .oe_(sram_oe_), .we_(~sram_oe_) );
sram45C_mod #(.BANK(1)) sram1 (
.addr(addr), .data(bus_data[15:8]),
.ce1_(1'b0), .ce2(1'b1), .oe_(sram_oe_), .we_(~sram_oe_) );
//-------------------------------------------------------------------------
assign outputA = ( bus_oeA & ~bus_oeB) ? outputA_r : 16'bz;
assign outputB = (~bus_oeA & bus_oeB) ? outputB_r : 16'bz;
initial begin
$monitor("%6d addr %4h outA %4h bus_data %4h outB %4h",
$time, addr, outputA, bus_data, outputB);
$display(""); $display("side 'A' sending (CPU storing value)");
sram_oe_ = 1'b1; bus_oeA = 1'b1; bus_oeB = 1'b0;
addr = 16'h0006;
outputA_r = 16'hA5f4;
#100;
addr = 16'h0007;
outputA_r = 16'h1749;
#100;
$display(""); $display("side 'B' sending (CPU loading value)");
sram_oe_ = 1'b0; bus_oeA = 1'b0; bus_oeB = 1'b1;
addr = 16'h0007;
//outputB_r = 16'h1234; // see if this value shows on bus with SRAM not connected
#100;
addr = 16'h0006;
//outputB_r = 16'h3465; // see if this value shows on bus with SRAM not connected
#100;
addr = 16'h0020;
#100;
addr = 16'h0021;
#100;
addr = 16'h0022;
#100;
addr = 16'h0023;
#100;
$display(""); $display("bus disabled, everything HiZ");
bus_oeA = 1'b0; bus_oeB = 1'b0;
addr = 16'h1111;
#100;
$display("");
$finish();
end
//-------------------------------------------------------------------------
endmodule
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
module sram45C_mod (addr, data, ce1_, ce2, oe_, we_);
// Modeled after Cypress CY62128EV30, using ISSI 64LV6416L's Verilog as a starter.
parameter BANK = 0;
input ce1_, ce2, oe_, we_;
input [15:0] addr;
inout [7:0] data; // bidirectional data
//-------------------------------------------------------------------------
reg [7:0] mem [0:65535];
//-------------------------------------------------------------------------
wire [7:0] data_out;
//-------------------------------------------------------------------------
integer k;
initial begin
if (BANK == 0) $readmemh("input\\ROM_0.img", mem);
else $readmemh("input\\ROM_1.img", mem);
for (k = 16'h0010; k < 16'h0030; k = k+ 1)
$display(" sram[%1d] mem[%4h] = %2H", BANK, k, mem[k]);
$display("");
end
//-------------------------------------------------------------------------
wire w_en = (~ce1_ & ce2 & ~we_ & oe_);
wire r_en = (~ce1_ & ce2 & we_ & ~oe_);
assign #45 data = (r_en) ? data_out : 8'hz;
assign data_out = mem[addr];
always @ ( addr ) begin // diagnostic only
#1; $display(" %6d addr sram[%1d] mem[%4h] = %2h w_en %b r_en %b",
$time, BANK, addr, mem[addr], w_en, r_en);
end
always @ ( data or data_out ) begin // diagnostic only
#1; $display(" %6d data %4h data_out %4h w_en %b r_en %b",
$time, data, data_out, w_en, r_en);
end
always @ ( addr or w_en ) begin // actual 'write' block, with diagnostic printout
if (w_en) begin
#45 mem[addr] = data;
#1; $display(" %6d stor sram[%1d] mem[%4h] = %2h w_en %b r_en %b",
$time, BANK, addr, mem[addr], w_en, r_en);
end
end
endmodule