设计一个存储器读写模块,模块功能如下:
存储模块总容量4KB,位宽32bit,考虑到节省功耗,将存储空间划分为4块memory实现,每块存储器1KB即256*32大小的存储块。4块memory依次编址
地址空间(字对齐地址) |
|
0x300 ~ 0x3ff | Mem3 |
0x200 ~ 0x2ff | Mem2 |
0x100 ~ 0x1ff | Mem1 |
0x000 ~ 0x0ff | Mem0 |
存储器读写模块需要实现的功能如下:
序号 | 操作名称 | 功能说明 |
1 | Load Byte | Byte数据装载,根据byte地址读取出相应8bit字节,经过符号位扩展后形成一个32bit数输出 |
2 | Load half word | 半字数据装载,根据半字地址(地址最低位为0)读取出相应16bit半字,经过符号位扩展后形成一个32bit数输出 |
3 | Load word | 整字数据装载,根据字地址(地址最低2bit为0)读取出一个32bit数输出 |
4 | Store Byte | Byte数据存储,将输入32bit数据的低8位根据byte地址存入存储器 |
5 | Store half word | 半字数据存储,将输入32bit数据的低16位根据半字地址存入存储器 |
6 | Store word | 整字数据装载,将输入32bit数据存入存储器 |
存储器读写模块的接口如下:
信号名 | 位宽 | I/O | 描述 |
clk | 1 | I | 时钟 |
nrst | 1 | I | 全局异步时钟,低电平有效,有效时所有寄存器清零 |
stall | 1 | I | 暂停信号,该信号为1时,不进行任何读写操作 |
op_code | 3 | I | 读写操作类型,对应功能表格各个功能 |
RWaddr | 14 | I | Byte对齐地址 |
wdata | 32 | I | 写端口数据 |
rdata | 32 | O | 从memory读出的32bit数据 |
请用verilog搭建memory模型后进行模块例化调用,1KB存储器规模为256*32bit,其接口及功能定义如下:
信号名 | 位宽 | I/O | 描述 |
CLK | 1 | I | 时钟 |
CEN | 1 | I | mem使能信号,低电平有效 |
WEN | 1 | I | 写使能信号,低电平有效 |
BWEN | 32 | I | Bit-write enable,按bit写使能 |
A | 8 | I | 读写地址 |
D | 32 | I | 写数据 |
Q | 32 | O | 读数据 |
mem的读写波形如下:
dmem.v
module memory #(
// --------------------------------------------------------------------------
// Parameter Declarations
// --------------------------------------------------------------------------
parameter AW = 8
)
(
input CLK,
input CEN,
input WEN,
input [31:0]BWEN,
input [AW-1:0]A,
input [31:0]D,
output reg [31:0]Q
);
// -----------------------------------------------------------------------------
// Constant Declarations
// -----------------------------------------------------------------------------
//localparam AWT = ((1<<AW)-1);
// Memory Array
reg [31:0]mem[255:0];
initial $readmemh("D:/workspace/verilog/EDA/RISC-V_CPU_2019/RISC-V_CPU_2019.srcs/sources_1/new/dmem.hex", mem);
always @ (posedge CLK)
begin
if(CEN)
Q <= 32'h0;
else
Q <= mem[A];
end
always @ (posedge CLK)
begin
if(~CEN & ~WEN )
begin
if (~BWEN[0])
mem[A][0] <= D[0];
if (~BWEN[1])
mem[A][1] <= D[1];
if (~BWEN[2])
mem[A][2] <= D[2];
if (~BWEN[3])
mem[A][3] <= D[3];
if (~BWEN[4])
mem[A][4] <= D[4];
if (~BWEN[5])
mem[A][5] <= D[5];
if (~BWEN[6])
mem[A][6] <= D[6];
if (~BWEN[7])
mem[A][7] <= D[7];
if (~BWEN[8])
mem[A][8] <= D[8];
if (~BWEN[9])
mem[A][9] <= D[9];
if (~BWEN[10])
mem[A][10] <= D[10];
if (~BWEN[11])
mem[A][11] <= D[11];
if (~BWEN[12])
mem[A][12] <= D[12];
if (~BWEN[13])
mem[A][13] <= D[13];
if (~BWEN[14])
mem[A][14] <= D[14];
if (~BWEN[15])
mem[A][15] <= D[15];
if (~BWEN[16])
mem[A][16] <= D[16];
if (~BWEN[17])
mem[A][17] <= D[17];
if (~BWEN[18])
mem[A][18] <= D[18];
if (~BWEN[19])
mem[A][19] <= D[19];
if (~BWEN[20])
mem[A][20] <= D[20];
if (~BWEN[21])
mem[A][21] <= D[21];
if (~BWEN[22])
mem[A][22] <= D[22];
if (~BWEN[23])
mem[A][23] <= D[23];
if (~BWEN[24])
mem[A][24] <= D[24];
if (~BWEN[25])
mem[A][25] <= D[25];
if (~BWEN[26])
mem[A][26] <= D[26];
if (~BWEN[27])
mem[A][27] <= D[27];
if (~BWEN[28])
mem[A][28] <= D[28];
if (~BWEN[29])
mem[A][29] <= D[29];
if (~BWEN[30])
mem[A][30] <= D[30];
if (~BWEN[31])
mem[A][31] <= D[31];
end
end
endmodule
dmem_ctrl.v
module mem_ctrl(
input clk,
input nrst,
input stall,
input [2:0]op_code,
input [11:0]RWaddr,
input [31:0]wdata,
output rdata
);
reg [31:0]rdata = 32'h0;
wire CEN1;
wire CEN2;
wire CEN3;
wire CEN4;
wire WEN;
wire [31:0]BWEN;
wire [31:0] Q1;
wire [31:0] Q2;
wire [31:0] Q3;
wire [31:0] Q4;
wire [31:0] Q;
assign CEN1 = RWaddr[11] | RWaddr[10];
assign CEN2 = RWaddr[11] | ~RWaddr[10];
assign CEN3 = ~RWaddr[11] | RWaddr[10];
assign CEN4 = ~RWaddr[11] | ~RWaddr[10];
assign WEN = ~op_code[2];
assign BWEN[7:0] = op_code[2]?8'h00:8'hff;
assign BWEN[15:8] = (op_code[2] & (op_code[1] ^ op_code[0]))?8'h00:8'hff;
assign BWEN[31:16] = (op_code[2] & op_code[1] & ~op_code[0])?16'h0000:16'hffff;
memory mem1(.CLK(clk), .CEN(CEN1), .WEN(WEN), .BWEN(BWEN), .A(RWaddr[9:2]), .D(wdata), .Q(Q1));
memory mem2(.CLK(clk), .CEN(CEN2), .WEN(WEN), .BWEN(BWEN), .A(RWaddr[9:2]), .D(wdata), .Q(Q2));
memory mem3(.CLK(clk), .CEN(CEN3), .WEN(WEN), .BWEN(BWEN), .A(RWaddr[9:2]), .D(wdata), .Q(Q3));
memory mem4(.CLK(clk), .CEN(CEN4), .WEN(WEN), .BWEN(BWEN), .A(RWaddr[9:2]), .D(wdata), .Q(Q4));
wire [31:0]out1;
wire [31:0]out2;
assign out1 = RWaddr[10]?Q2:Q1;
assign out2 = RWaddr[10]?Q4:Q3;
assign Q = RWaddr[11]?out2:out1;
always @ (posedge clk)
begin
case(op_code)
3'b000://load_byte
case(RWaddr[1:0])
2'b00:rdata = {{24{Q[7]}},Q[7:0]};
2'b01:rdata = {{24{Q[15]}},Q[15:8]};
2'b10:rdata = {{24{Q[23]}},Q[23:16]};
2'b11:rdata = {{24{Q[31]}},Q[31:24]};
endcase
3'b001://load_halfword
if (RWaddr[1])
rdata = {{16{Q[31]}},Q[31:16]};
else
rdata = {{16{Q[15]}},Q[15:0]};
3'b010://load word
rdata = Q;
// default:rdata = 32'hffffffff;
endcase
end
endmodule
mem_ctrl_tb.v
`timescale 1ns / 1ps
module mem_ctrl_tb(
);
reg clk;
reg nrst;
reg stall;
reg [2:0] op_code;
reg [11:0] RWaddr;
reg [31:0] wdata;
wire [31:0] rdata;
mem_ctrl ram0(
clk,
nrst,
stall,
op_code,
RWaddr,
wdata,
rdata
);
always begin
#10 clk = ~clk;
end
initial begin
clk = 1'b1;
nrst = 1'b1;
stall = 1'b0;
op_code = 3'b110;
RWaddr = 12'b0000_0000_0100;
wdata = 32'hff00ff00;
#30 begin
op_code = 3'b110;
RWaddr = 12'b0000_0000_1000;
wdata = 32'hf000000f;
end
#60 begin
op_code = 3'b110;
RWaddr = 12'b0000_0000_0100;
wdata = 32'hff0000ff;
end
#60 begin
op_code = 3'b010;
RWaddr = 14'b0000_0000_1000;
end
#60 begin
op_code = 3'b010;
RWaddr = 14'b0000_0000_0100;
end
end
endmodule