[examples] Add Dual-Port Memory to Simple System

This commit adds a separate memory ports for instruction and data
fetches to the Simple System example.

* Add Dual-Port RAM with 1 cycle read/write delay, 32 bit words.

* Introduce parametric signal width definitions for bus implementation
        to work with a single host / device.

* Modify Simple System top module to instantiate the new dual-port RAM.
This commit is contained in:
ganoam 2020-01-28 13:30:02 +01:00 committed by Pirmin Vogel
parent 230c282c36
commit 86979e603f
4 changed files with 172 additions and 30 deletions

View file

@ -24,8 +24,7 @@ module ibex_simple_system (
logic clk_sys = 1'b0, rst_sys_n;
typedef enum {
CoreD,
CoreI
CoreD
} bus_host_e;
typedef enum {
@ -35,7 +34,7 @@ module ibex_simple_system (
} bus_device_e;
localparam NrDevices = 3;
localparam NrHosts = 2;
localparam NrHosts = 1;
// interrupts
logic timer_irq;
@ -71,6 +70,16 @@ module ibex_simple_system (
assign cfg_device_addr_base[Timer] = 32'h30000;
assign cfg_device_addr_mask[Timer] = ~32'h3FF; // 1 kB
// Instruction fetch signals
logic instr_req;
logic instr_gnt;
logic instr_rvalid;
logic [31:0] instr_addr;
logic [31:0] instr_rdata;
logic instr_err;
assign instr_gnt = instr_req;
assign instr_err = '0;
`ifdef VERILATOR
assign clk_sys = IO_CLK;
@ -123,10 +132,6 @@ module ibex_simple_system (
.cfg_device_addr_mask
);
assign host_we[CoreI] = 1'b0;
assign host_be[CoreI] = 4'b1111;
assign host_wdata[CoreI] = 32'b0;
ibex_core_tracing #(
.MHPMCounterNum(29),
.DmHaltAddr(32'h00100000),
@ -143,12 +148,12 @@ module ibex_simple_system (
// First instruction executed is at 0x0 + 0x80
.boot_addr_i (32'h00100000),
.instr_req_o (host_req[CoreI]),
.instr_gnt_i (host_gnt[CoreI]),
.instr_rvalid_i (host_rvalid[CoreI]),
.instr_addr_o (host_addr[CoreI]),
.instr_rdata_i (host_rdata[CoreI]),
.instr_err_i (host_err[CoreI]),
.instr_req_o (instr_req),
.instr_gnt_i (instr_gnt),
.instr_rvalid_i (instr_rvalid),
.instr_addr_o (instr_addr),
.instr_rdata_i (instr_rdata),
.instr_err_i (instr_err),
.data_req_o (host_req[CoreD]),
.data_gnt_i (host_gnt[CoreD]),
@ -173,18 +178,27 @@ module ibex_simple_system (
);
// SRAM block for instruction and data storage
ram_1p #(
ram_2p #(
.Depth(1024*1024/4)
) u_ram (
.clk_i (clk_sys),
.rst_ni (rst_sys_n),
.req_i (device_req[Ram]),
.we_i (device_we[Ram]),
.be_i (device_be[Ram]),
.addr_i (device_addr[Ram]),
.wdata_i (device_wdata[Ram]),
.rvalid_o (device_rvalid[Ram]),
.rdata_o (device_rdata[Ram])
.clk_i (clk_sys),
.rst_ni (rst_sys_n),
.a_req_i (device_req[Ram]),
.a_we_i (device_we[Ram]),
.a_be_i (device_be[Ram]),
.a_addr_i (device_addr[Ram]),
.a_wdata_i (device_wdata[Ram]),
.a_rvalid_o (device_rvalid[Ram]),
.a_rdata_o (device_rdata[Ram]),
.b_req_i (instr_req),
.b_we_i (1'b0),
.b_be_i (4'b0),
.b_addr_i (instr_addr),
.b_wdata_i (32'b0),
.b_rvalid_o (instr_rvalid),
.b_rdata_o (instr_rdata)
);
simulator_ctrl #(

View file

@ -51,14 +51,17 @@ module bus #(
input [AddressWidth-1:0] cfg_device_addr_mask [NrDevices]
);
logic [$clog2(NrHosts)-1:0] host_sel_req, host_sel_resp;
logic [$clog2(NrDevices)-1:0] device_sel_req, device_sel_resp;
localparam int unsigned NumBitsHostSel = NrHosts > 1 ? $clog2(NrHosts) : 1;
localparam int unsigned NumBitsDeviceSel = NrDevices > 1 ? $clog2(NrDevices) : 1;
logic [NumBitsHostSel-1:0] host_sel_req, host_sel_resp;
logic [NumBitsDeviceSel-1:0] device_sel_req, device_sel_resp;
// Master select prio arbiter
always_comb begin
for (integer host = NrHosts - 1; host >= 0; host = host - 1) begin
if (host_req_i[host]) begin
host_sel_req = $clog2(NrHosts)'(host);
host_sel_req = NumBitsHostSel'(host);
end
end
end
@ -68,7 +71,7 @@ module bus #(
for (integer device = 0; device < NrDevices; device = device + 1) begin
if ((host_addr_i[host_sel_req] & cfg_device_addr_mask[device])
== cfg_device_addr_base[device]) begin
device_sel_req = $clog2(NrDevices)'(device);
device_sel_req = NumBitsDeviceSel'(device);
end
end
end
@ -86,7 +89,7 @@ module bus #(
always_comb begin
for (integer device = 0; device < NrDevices; device = device + 1) begin
if ($clog2(NrDevices)'(device) == device_sel_req) begin
if (NumBitsDeviceSel'(device) == device_sel_req) begin
device_req_o[device] = host_req_i[host_sel_req];
device_we_o[device] = host_we_i[host_sel_req];
device_addr_o[device] = host_addr_i[host_sel_req];
@ -105,8 +108,7 @@ module bus #(
always_comb begin
for (integer host = 0; host < NrHosts; host = host + 1) begin
host_gnt_o[host] = 1'b0;
if ($clog2(NrHosts)'(host) == host_sel_resp) begin
if (NumBitsHostSel'(host) == host_sel_resp) begin
host_rvalid_o[host] = device_rvalid_i[device_sel_resp];
host_err_o[host] = device_err_i[device_sel_resp];
host_rdata_o[host] = device_rdata_i[device_sel_resp];

125
shared/rtl/ram_2p.sv Normal file
View file

@ -0,0 +1,125 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
/**
* Dual-port RAM with 1 cycle read/write delay, 32 bit words.
*
* The two ports are in Read-First Mode: when reading from and writing to the same address
* simultaneously, the old content is returned, before the new content is written. New content
* is made available to both ports with one cycle delay.
*
* Simultaneous write operations by both ports to the same address are to be avoided: The data
* written to memory is not determined.
*/
module ram_2p #(
parameter int Depth = 128
) (
input clk_i,
input rst_ni,
input a_req_i,
input a_we_i,
input [ 3:0] a_be_i,
input [31:0] a_addr_i,
input [31:0] a_wdata_i,
output logic a_rvalid_o,
output logic [31:0] a_rdata_o,
input b_req_i,
input b_we_i,
input [ 3:0] b_be_i,
input [31:0] b_addr_i,
input [31:0] b_wdata_i,
output logic b_rvalid_o,
output logic [31:0] b_rdata_o
);
localparam int Aw = $clog2(Depth);
logic [31:0] mem [Depth];
logic [Aw-1:0] a_addr_idx;
assign a_addr_idx = a_addr_i[Aw-1+2:2];
logic [31-Aw:0] unused_a_addr_parts;
assign unused_a_addr_parts = {a_addr_i[31:Aw+2], a_addr_i[1:0]};
logic [Aw-1:0] b_addr_idx;
assign b_addr_idx = b_addr_i[Aw-1+2:2];
logic [31-Aw:0] unused_b_addr_parts;
assign unused_b_addr_parts = {b_addr_i[31:Aw+2], b_addr_i[1:0]};
always @(posedge clk_i) begin
if (a_req_i) begin
if (a_we_i) begin
for (int i = 0; i < 4; i = i + 1) begin
if (a_be_i[i] == 1'b1) begin
mem[a_addr_idx][i*8 +: 8] <= a_wdata_i[i*8 +: 8];
end
end
end
a_rdata_o <= mem[a_addr_idx];
end
end
always @(posedge clk_i) begin
if (b_req_i) begin
if (b_we_i) begin
for (int i = 0; i < 4; i = i + 1) begin
if (b_be_i[i] == 1'b1) begin
mem[b_addr_idx][i*8 +: 8] <= b_wdata_i[i*8 +: 8];
end
end
end
b_rdata_o <= mem[b_addr_idx];
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
a_rvalid_o <= '0;
end else begin
a_rvalid_o <= a_req_i;
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
b_rvalid_o <= '0;
end else begin
b_rvalid_o <= b_req_i;
end
end
`ifdef VERILATOR
// Task for loading 'mem' with SystemVerilog system task $readmemh()
export "DPI-C" task simutil_verilator_memload;
// Function for setting a specific 32 bit element in |mem|
// Returns 1 (true) for success, 0 (false) for errors.
export "DPI-C" function simutil_verilator_set_mem;
task simutil_verilator_memload;
input string file;
$readmemh(file, mem);
endtask
// TODO: Allow 'val' to have other widths than 32 bit
function int simutil_verilator_set_mem(input int index,
input logic[31:0] val);
if (index >= Depth) begin
return 0;
end
mem[index] = val;
return 1;
endfunction
`endif
`ifdef SRAM_INIT_FILE
localparam MEM_FILE = `"`SRAM_INIT_FILE`";
initial begin
$display("Initializing SRAM from %s", MEM_FILE);
$readmemh(MEM_FILE, mem);
end
`endif
endmodule

View file

@ -11,6 +11,7 @@ filesets:
files:
- ./rtl/prim_clock_gating.sv
- ./rtl/ram_1p.sv
- ./rtl/ram_2p.sv
- ./rtl/bus.sv
- ./rtl/sim/simulator_ctrl.sv
- ./rtl/timer.sv