Commit 007cb530 authored by YouGuoliang's avatar YouGuoliang
Browse files

Add Lab03

parent 6519d3e6
# cache 实验所需的文件
* main_mem.sv 和 mem.sv 是我们提供的主存代码,cache代码会调用它们
* cache.sv 是我们提供的直接相连cache的代码。
* generate_cache_tb.py 是我们提供的,用于生成 cache 测试testbench的python代码
* cache_tb.sv 是一个由 generate_cache_tb.py 生成的testbench
module cache #(
parameter LINE_ADDR_LEN = 3, // line内地址长度,决定了每个line具有2^3个word
parameter SET_ADDR_LEN = 3, // 组地址长度,决定了一共有2^3=8组
parameter TAG_ADDR_LEN = 6, // tag长度
parameter WAY_CNT = 3 // 组相连度,决定了每组中有多少路line,这里是直接映射型cache,因此该参数没用到
)(
input clk, rst,
output miss, // 对CPU发出的miss信号
input [31:0] addr, // 读写请求地址
input rd_req, // 读请求信号
output reg [31:0] rd_data, // 读出的数据,一次读一个word
input wr_req, // 写请求信号
input [31:0] wr_data // 要写入的数据,一次写一个word
);
localparam MEM_ADDR_LEN = TAG_ADDR_LEN + SET_ADDR_LEN ; // 计算主存地址长度 MEM_ADDR_LEN,主存大小=2^MEM_ADDR_LEN个line
localparam UNUSED_ADDR_LEN = 32 - TAG_ADDR_LEN - SET_ADDR_LEN - LINE_ADDR_LEN - 2 ; // 计算未使用的地址的长度
localparam LINE_SIZE = 1 << LINE_ADDR_LEN ; // 计算 line 中 word 的数量,即 2^LINE_ADDR_LEN 个word 每 line
localparam SET_SIZE = 1 << SET_ADDR_LEN ; // 计算一共有多少组,即 2^SET_ADDR_LEN 个组
reg [ 31:0] cache_mem [SET_SIZE][LINE_SIZE]; // SET_SIZE个line,每个line有LINE_SIZE个word
reg [TAG_ADDR_LEN-1:0] cache_tags [SET_SIZE]; // SET_SIZE个TAG
reg valid [SET_SIZE]; // SET_SIZE个valid(有效位)
reg dirty [SET_SIZE]; // SET_SIZE个dirty(脏位)
wire [ 2-1:0] word_addr; // 将输入地址addr拆分成这5个部分
wire [ LINE_ADDR_LEN-1:0] line_addr;
wire [ SET_ADDR_LEN-1:0] set_addr;
wire [ TAG_ADDR_LEN-1:0] tag_addr;
wire [UNUSED_ADDR_LEN-1:0] unused_addr;
enum {IDLE, SWAP_OUT, SWAP_IN, SWAP_IN_OK} cache_stat; // cache 状态机的状态定义
// IDLE代表就绪,SWAP_OUT代表正在换出,SWAP_IN代表正在换入,SWAP_IN_OK代表换入后进行一周期的写入cache操作。
reg [ SET_ADDR_LEN-1:0] mem_rd_set_addr = 0;
reg [ TAG_ADDR_LEN-1:0] mem_rd_tag_addr = 0;
wire [ MEM_ADDR_LEN-1:0] mem_rd_addr = {mem_rd_tag_addr, mem_rd_set_addr};
reg [ MEM_ADDR_LEN-1:0] mem_wr_addr = 0;
reg [31:0] mem_wr_line [LINE_SIZE];
wire [31:0] mem_rd_line [LINE_SIZE];
wire mem_gnt; // 主存响应读写的握手信号
assign {unused_addr, tag_addr, set_addr, line_addr, word_addr} = addr; // 拆分 32bit ADDR
reg cache_hit = 1'b0;
always @ (*) begin // 判断 输入的address 是否在 cache 中命中
if(valid[set_addr] && cache_tags[set_addr] == tag_addr) // 如果 cache line有效,并且tag与输入地址中的tag相等,则命中
cache_hit = 1'b1;
else
cache_hit = 1'b0;
end
always @ (posedge clk or posedge rst) begin // ?? cache ???
if(rst) begin
cache_stat <= IDLE;
for(integer i = 0; i < SET_SIZE; i++) begin
dirty[i] = 1'b0;
valid[i] = 1'b0;
end
for(integer k = 0; k < LINE_SIZE; k++)
mem_wr_line[k] <= 0;
mem_wr_addr <= 0;
{mem_rd_tag_addr, mem_rd_set_addr} <= 0;
rd_data <= 0;
end else begin
case(cache_stat)
IDLE: begin
if(cache_hit) begin
if(rd_req) begin // 如果cache命中,并且是读请求,
rd_data <= cache_mem[set_addr][line_addr]; //则直接从cache中取出要读的数据
end else if(wr_req) begin // 如果cache命中,并且是写请求,
cache_mem[set_addr][line_addr] <= wr_data; // 则直接向cache中写入数据
dirty[set_addr] <= 1'b1; // 写数据的同时置脏位
end
end else begin
if(wr_req | rd_req) begin // 如果 cache 未命中,并且有读写请求,则需要进行换入
if(valid[set_addr] & dirty[set_addr]) begin // 如果 要换入的cache line 本来有效,且脏,则需要先将它换出
cache_stat <= SWAP_OUT;
mem_wr_addr <= {cache_tags[set_addr], set_addr};
mem_wr_line <= cache_mem[set_addr];
end else begin // 反之,不需要换出,直接换入
cache_stat <= SWAP_IN;
end
{mem_rd_tag_addr, mem_rd_set_addr} <= {tag_addr, set_addr};
end
end
end
SWAP_OUT: begin
if(mem_gnt) begin // 如果主存握手信号有效,说明换出成功,跳到下一状态
cache_stat <= SWAP_IN;
end
end
SWAP_IN: begin
if(mem_gnt) begin // 如果主存握手信号有效,说明换入成功,跳到下一状态
cache_stat <= SWAP_IN_OK;
end
end
SWAP_IN_OK: begin // 上一个周期换入成功,这周期将主存读出的line写入cache,并更新tag,置高valid,置低dirty
for(integer i=0; i<LINE_SIZE; i++) cache_mem[mem_rd_set_addr][i] <= mem_rd_line[i];
cache_tags[mem_rd_set_addr] <= mem_rd_tag_addr;
valid [mem_rd_set_addr] <= 1'b1;
dirty [mem_rd_set_addr] <= 1'b0;
cache_stat <= IDLE; // 回到就绪状态
end
endcase
end
end
wire mem_rd_req = (cache_stat == SWAP_IN );
wire mem_wr_req = (cache_stat == SWAP_OUT);
wire [ MEM_ADDR_LEN-1 :0] mem_addr = mem_rd_req ? mem_rd_addr : ( mem_wr_req ? mem_wr_addr : 0);
assign miss = (rd_req | wr_req) & ~(cache_hit && cache_stat==IDLE) ; // 当 有读写请求时,如果cache不处于就绪(IDLE)状态,或者未命中,则miss=1
main_mem #( // 主存,每次读写以line 为单位
.LINE_ADDR_LEN ( LINE_ADDR_LEN ),
.ADDR_LEN ( MEM_ADDR_LEN )
) main_mem_instance (
.clk ( clk ),
.rst ( rst ),
.gnt ( mem_gnt ),
.addr ( mem_addr ),
.rd_req ( mem_rd_req ),
.rd_line ( mem_rd_line ),
.wr_req ( mem_wr_req ),
.wr_line ( mem_wr_line )
);
endmodule
`timescale 1ns/100ps
//correct read result:
// 0000000c 00000011 0000000c 00000005 0000000b 00000000 00000008 00000005
module cache_tb();
`define DATA_COUNT (8)
`define RDWR_COUNT (6*`DATA_COUNT)
reg wr_cycle [`RDWR_COUNT];
reg rd_cycle [`RDWR_COUNT];
reg [31:0] addr_rom [`RDWR_COUNT];
reg [31:0] wr_data_rom [`RDWR_COUNT];
reg [31:0] validation_data [`DATA_COUNT];
initial begin
// 8 sequence write cycles
rd_cycle[ 0] = 1'b0; wr_cycle[ 0] = 1'b1; addr_rom[ 0]='h00000000; wr_data_rom[ 0]='h0000001b;
rd_cycle[ 1] = 1'b0; wr_cycle[ 1] = 1'b1; addr_rom[ 1]='h00000004; wr_data_rom[ 1]='h0000001c;
rd_cycle[ 2] = 1'b0; wr_cycle[ 2] = 1'b1; addr_rom[ 2]='h00000008; wr_data_rom[ 2]='h00000014;
rd_cycle[ 3] = 1'b0; wr_cycle[ 3] = 1'b1; addr_rom[ 3]='h0000000c; wr_data_rom[ 3]='h00000005;
rd_cycle[ 4] = 1'b0; wr_cycle[ 4] = 1'b1; addr_rom[ 4]='h00000010; wr_data_rom[ 4]='h00000016;
rd_cycle[ 5] = 1'b0; wr_cycle[ 5] = 1'b1; addr_rom[ 5]='h00000014; wr_data_rom[ 5]='h00000002;
rd_cycle[ 6] = 1'b0; wr_cycle[ 6] = 1'b1; addr_rom[ 6]='h00000018; wr_data_rom[ 6]='h00000008;
rd_cycle[ 7] = 1'b0; wr_cycle[ 7] = 1'b1; addr_rom[ 7]='h0000001c; wr_data_rom[ 7]='h00000003;
// 24 random read and write cycles
rd_cycle[ 8] = 1'b0; wr_cycle[ 8] = 1'b1; addr_rom[ 8]='h00000008; wr_data_rom[ 8]='h0000000f;
rd_cycle[ 9] = 1'b0; wr_cycle[ 9] = 1'b1; addr_rom[ 9]='h00000008; wr_data_rom[ 9]='h0000000c;
rd_cycle[ 10] = 1'b0; wr_cycle[ 10] = 1'b1; addr_rom[ 10]='h0000001c; wr_data_rom[ 10]='h00000013;
rd_cycle[ 11] = 1'b1; wr_cycle[ 11] = 1'b0; addr_rom[ 11]='h0000001c; wr_data_rom[ 11]='h00000000;
rd_cycle[ 12] = 1'b1; wr_cycle[ 12] = 1'b0; addr_rom[ 12]='h0000001c; wr_data_rom[ 12]='h00000000;
rd_cycle[ 13] = 1'b1; wr_cycle[ 13] = 1'b0; addr_rom[ 13]='h00000008; wr_data_rom[ 13]='h00000000;
rd_cycle[ 14] = 1'b1; wr_cycle[ 14] = 1'b0; addr_rom[ 14]='h00000008; wr_data_rom[ 14]='h00000000;
rd_cycle[ 15] = 1'b1; wr_cycle[ 15] = 1'b0; addr_rom[ 15]='h00000000; wr_data_rom[ 15]='h00000000;
rd_cycle[ 16] = 1'b0; wr_cycle[ 16] = 1'b1; addr_rom[ 16]='h00000018; wr_data_rom[ 16]='h00000008;
rd_cycle[ 17] = 1'b1; wr_cycle[ 17] = 1'b0; addr_rom[ 17]='h00000010; wr_data_rom[ 17]='h00000000;
rd_cycle[ 18] = 1'b0; wr_cycle[ 18] = 1'b1; addr_rom[ 18]='h00000000; wr_data_rom[ 18]='h0000001e;
rd_cycle[ 19] = 1'b1; wr_cycle[ 19] = 1'b0; addr_rom[ 19]='h00000014; wr_data_rom[ 19]='h00000000;
rd_cycle[ 20] = 1'b1; wr_cycle[ 20] = 1'b0; addr_rom[ 20]='h00000000; wr_data_rom[ 20]='h00000000;
rd_cycle[ 21] = 1'b1; wr_cycle[ 21] = 1'b0; addr_rom[ 21]='h00000000; wr_data_rom[ 21]='h00000000;
rd_cycle[ 22] = 1'b0; wr_cycle[ 22] = 1'b1; addr_rom[ 22]='h0000001c; wr_data_rom[ 22]='h00000005;
rd_cycle[ 23] = 1'b1; wr_cycle[ 23] = 1'b0; addr_rom[ 23]='h00000008; wr_data_rom[ 23]='h00000000;
rd_cycle[ 24] = 1'b0; wr_cycle[ 24] = 1'b1; addr_rom[ 24]='h00000014; wr_data_rom[ 24]='h00000000;
rd_cycle[ 25] = 1'b1; wr_cycle[ 25] = 1'b0; addr_rom[ 25]='h00000008; wr_data_rom[ 25]='h00000000;
rd_cycle[ 26] = 1'b0; wr_cycle[ 26] = 1'b1; addr_rom[ 26]='h00000000; wr_data_rom[ 26]='h0000000c;
rd_cycle[ 27] = 1'b1; wr_cycle[ 27] = 1'b0; addr_rom[ 27]='h00000010; wr_data_rom[ 27]='h00000000;
rd_cycle[ 28] = 1'b0; wr_cycle[ 28] = 1'b1; addr_rom[ 28]='h00000010; wr_data_rom[ 28]='h0000000b;
rd_cycle[ 29] = 1'b1; wr_cycle[ 29] = 1'b0; addr_rom[ 29]='h0000000c; wr_data_rom[ 29]='h00000000;
rd_cycle[ 30] = 1'b0; wr_cycle[ 30] = 1'b1; addr_rom[ 30]='h00000004; wr_data_rom[ 30]='h00000011;
rd_cycle[ 31] = 1'b1; wr_cycle[ 31] = 1'b0; addr_rom[ 31]='h0000001c; wr_data_rom[ 31]='h00000000;
// 8 silence cycles
rd_cycle[ 32] = 1'b0; wr_cycle[ 32] = 1'b0; addr_rom[ 32]='h00000000; wr_data_rom[ 32]='h00000000;
rd_cycle[ 33] = 1'b0; wr_cycle[ 33] = 1'b0; addr_rom[ 33]='h00000000; wr_data_rom[ 33]='h00000000;
rd_cycle[ 34] = 1'b0; wr_cycle[ 34] = 1'b0; addr_rom[ 34]='h00000000; wr_data_rom[ 34]='h00000000;
rd_cycle[ 35] = 1'b0; wr_cycle[ 35] = 1'b0; addr_rom[ 35]='h00000000; wr_data_rom[ 35]='h00000000;
rd_cycle[ 36] = 1'b0; wr_cycle[ 36] = 1'b0; addr_rom[ 36]='h00000000; wr_data_rom[ 36]='h00000000;
rd_cycle[ 37] = 1'b0; wr_cycle[ 37] = 1'b0; addr_rom[ 37]='h00000000; wr_data_rom[ 37]='h00000000;
rd_cycle[ 38] = 1'b0; wr_cycle[ 38] = 1'b0; addr_rom[ 38]='h00000000; wr_data_rom[ 38]='h00000000;
rd_cycle[ 39] = 1'b0; wr_cycle[ 39] = 1'b0; addr_rom[ 39]='h00000000; wr_data_rom[ 39]='h00000000;
// 8 sequence read cycles
rd_cycle[ 40] = 1'b1; wr_cycle[ 40] = 1'b0; addr_rom[ 40]='h00000000; wr_data_rom[ 40]='h00000000;
rd_cycle[ 41] = 1'b1; wr_cycle[ 41] = 1'b0; addr_rom[ 41]='h00000004; wr_data_rom[ 41]='h00000000;
rd_cycle[ 42] = 1'b1; wr_cycle[ 42] = 1'b0; addr_rom[ 42]='h00000008; wr_data_rom[ 42]='h00000000;
rd_cycle[ 43] = 1'b1; wr_cycle[ 43] = 1'b0; addr_rom[ 43]='h0000000c; wr_data_rom[ 43]='h00000000;
rd_cycle[ 44] = 1'b1; wr_cycle[ 44] = 1'b0; addr_rom[ 44]='h00000010; wr_data_rom[ 44]='h00000000;
rd_cycle[ 45] = 1'b1; wr_cycle[ 45] = 1'b0; addr_rom[ 45]='h00000014; wr_data_rom[ 45]='h00000000;
rd_cycle[ 46] = 1'b1; wr_cycle[ 46] = 1'b0; addr_rom[ 46]='h00000018; wr_data_rom[ 46]='h00000000;
rd_cycle[ 47] = 1'b1; wr_cycle[ 47] = 1'b0; addr_rom[ 47]='h0000001c; wr_data_rom[ 47]='h00000000;
end
initial begin
validation_data[ 0] = 'h0000000c;
validation_data[ 1] = 'h00000011;
validation_data[ 2] = 'h0000000c;
validation_data[ 3] = 'h00000005;
validation_data[ 4] = 'h0000000b;
validation_data[ 5] = 'h00000000;
validation_data[ 6] = 'h00000008;
validation_data[ 7] = 'h00000005;
end
reg clk = 1'b1, rst = 1'b1;
initial #4 rst = 1'b0;
always #1 clk = ~clk;
wire miss;
wire [31:0] rd_data;
reg [31:0] index = 0, wr_data = 0, addr = 0;
reg rd_req = 1'b0, wr_req = 1'b0;
reg rd_req_ff = 1'b0, miss_ff = 1'b0;
reg [31:0] validation_count = 0;
always @ (posedge clk or posedge rst)
if(rst) begin
rd_req_ff <= 1'b0;
miss_ff <= 1'b0;
end else begin
rd_req_ff <= rd_req;
miss_ff <= miss;
end
always @ (posedge clk or posedge rst)
if(rst) begin
validation_count <= 0;
end else begin
if(validation_count>=`DATA_COUNT) begin
validation_count <= 'hffffffff;
end else if(rd_req_ff && (index>(4*`DATA_COUNT))) begin
if(~miss_ff) begin
if(validation_data[validation_count]==rd_data)
validation_count <= validation_count+1;
else
validation_count <= 0;
end
end else begin
validation_count <= 0;
end
end
always @ (posedge clk or posedge rst)
if(rst) begin
index <= 0;
wr_data <= 0;
addr <= 0;
rd_req <= 1'b0;
wr_req <= 1'b0;
end else begin
if(~miss) begin
if(index<`RDWR_COUNT) begin
if(wr_cycle[index]) begin
rd_req <= 1'b0;
wr_req <= 1'b1;
end else if(rd_cycle[index]) begin
wr_data <= 0;
rd_req <= 1'b1;
wr_req <= 1'b0;
end else begin
wr_data <= 0;
rd_req <= 1'b0;
wr_req <= 1'b0;
end
wr_data <= wr_data_rom[index];
addr <= addr_rom[index];
index <= index + 1;
end else begin
wr_data <= 0;
addr <= 0;
rd_req <= 1'b0;
wr_req <= 1'b0;
end
end
end
cache #(
.LINE_ADDR_LEN ( 3 ),
.SET_ADDR_LEN ( 2 ),
.TAG_ADDR_LEN ( 12 ),
.WAY_CNT ( 3 )
) cache_test_instance (
.clk ( clk ),
.rst ( rst ),
.miss ( miss ),
.addr ( addr ),
.rd_req ( rd_req ),
.rd_data ( rd_data ),
.wr_req ( wr_req ),
.wr_data ( wr_data )
);
endmodule
# -*- coding:utf-8 -*-
# Python2 or Python3
# Author : WangXuan
#
# 功能: 生成 cache_tb.v , 即针对cache的testbench
#
verilog_head = '''
module cache_tb();
`define DATA_COUNT (%d)
`define RDWR_COUNT (6*`DATA_COUNT)
reg wr_cycle [`RDWR_COUNT];
reg rd_cycle [`RDWR_COUNT];
reg [31:0] addr_rom [`RDWR_COUNT];
reg [31:0] wr_data_rom [`RDWR_COUNT];
reg [31:0] validation_data [`DATA_COUNT];
initial begin
'''
verilog_tail = '''
end
reg clk = 1'b1, rst = 1'b1;
initial #4 rst = 1'b0;
always #1 clk = ~clk;
wire miss;
wire [31:0] rd_data;
reg [31:0] index = 0, wr_data = 0, addr = 0;
reg rd_req = 1'b0, wr_req = 1'b0;
reg rd_req_ff = 1'b0, miss_ff = 1'b0;
reg [31:0] validation_count = 0;
always @ (posedge clk or posedge rst)
if(rst) begin
rd_req_ff <= 1'b0;
miss_ff <= 1'b0;
end else begin
rd_req_ff <= rd_req;
miss_ff <= miss;
end
always @ (posedge clk or posedge rst)
if(rst) begin
validation_count <= 0;
end else begin
if(validation_count>=`DATA_COUNT) begin
validation_count <= 'hffffffff;
end else if(rd_req_ff && (index>(4*`DATA_COUNT))) begin
if(~miss_ff) begin
if(validation_data[validation_count]==rd_data)
validation_count <= validation_count+1;
else
validation_count <= 0;
end
end else begin
validation_count <= 0;
end
end
always @ (posedge clk or posedge rst)
if(rst) begin
index <= 0;
wr_data <= 0;
addr <= 0;
rd_req <= 1'b0;
wr_req <= 1'b0;
end else begin
if(~miss) begin
if(index<`RDWR_COUNT) begin
if(wr_cycle[index]) begin
rd_req <= 1'b0;
wr_req <= 1'b1;
end else if(rd_cycle[index]) begin
wr_data <= 0;
rd_req <= 1'b1;
wr_req <= 1'b0;
end else begin
wr_data <= 0;
rd_req <= 1'b0;
wr_req <= 1'b0;
end
wr_data <= wr_data_rom[index];
addr <= addr_rom[index];
index <= index + 1;
end else begin
wr_data <= 0;
addr <= 0;
rd_req <= 1'b0;
wr_req <= 1'b0;
end
end
end
cache #(
.LINE_ADDR_LEN ( 3 ),
.SET_ADDR_LEN ( 2 ),
.TAG_ADDR_LEN ( 12 ),
.WAY_CNT ( 3 )
) cache_test_instance (
.clk ( clk ),
.rst ( rst ),
.miss ( miss ),
.addr ( addr ),
.rd_req ( rd_req ),
.rd_data ( rd_data ),
.wr_req ( wr_req ),
.wr_data ( wr_data )
);
endmodule
'''
import sys
from random import randint
if len(sys.argv) != 2:
print(' Usage:\n python generate_cache_tb.py [write words]')
print(' Example:\n python generate_cache_tb.py 16')
print(' Tip: use this command to write to file:\n python generate_cache_tb.py 16 > cache_tb.v')
else:
try:
N = int( sys.argv[1] )
except:
print(' *** Error: parameter must be integer, not %s' % (sys.argv[1], ) )
sys.exit(-1)
result = []
verilog = verilog_head % (N,)
verilog += " // %d sequence write cycles\n" % (N,)
for i in range( N ):
writeval = randint(0, 4*N)
result.append( writeval )
verilog += " rd_cycle[%5d] = 1'b0; wr_cycle[%5d] = 1'b1; addr_rom[%5d]='h%08x; wr_data_rom[%5d]='h%08x;\n" % (i, i, i, i*4, i, writeval)
verilog += " // %d random read and write cycles\n" % (3*N,)
for i in range( N, 4*N ):
rd_wr_addr = randint(0, N-1)
if randint(0,1)==0:
writeval = randint(0, 4*N)
result[rd_wr_addr] = writeval
verilog += " rd_cycle[%5d] = 1'b0; wr_cycle[%5d] = 1'b1; addr_rom[%5d]='h%08x; wr_data_rom[%5d]='h%08x;\n" % (i, i, i, rd_wr_addr*4, i, writeval)
else:
verilog += " rd_cycle[%5d] = 1'b1; wr_cycle[%5d] = 1'b0; addr_rom[%5d]='h%08x; wr_data_rom[%5d]='h%08x;\n" % (i, i, i, rd_wr_addr*4, i, 0)
verilog += " // %d silence cycles\n" % (N,)
for i in range( 4*N, 5*N ):
verilog += " rd_cycle[%5d] = 1'b0; wr_cycle[%5d] = 1'b0; addr_rom[%5d]='h%08x; wr_data_rom[%5d]='h%08x;\n" % (i, i, i, 0, i, 0)
verilog += " // %d sequence read cycles\n" % (N,)
for i in range( 5*N, 6*N ):
verilog += " rd_cycle[%5d] = 1'b1; wr_cycle[%5d] = 1'b0; addr_rom[%5d]='h%08x; wr_data_rom[%5d]='h%08x;\n" % (i, i, i, (i-5*N)*4, i, 0)
verilog += 'end\n\ninitial begin\n'
for i,res in enumerate(result):
verilog += " validation_data[%5d] = 'h%08x; \n" % (i, res)
verilog += verilog_tail
res_str = '`timescale 1ns/100ps\n//correct read result:\n//'
for res in result:
res_str += ' %08x' % (res, )
print(res_str)
print(verilog)
\ No newline at end of file
module main_mem #( // 每次读取一个line
parameter LINE_ADDR_LEN = 3, // line内地址长度,决定了每个line具有 2^LINE_ADDR_LEN 个word
parameter ADDR_LEN = 8 // 决定了mem中包含 2^ADDR_LEN 个line
)(
input clk, rst,
output gnt, // read or write grant
input [ADDR_LEN-1:0] addr, // line的地址
input rd_req,
output reg [31:0] rd_line [1 << LINE_ADDR_LEN],
input wr_req,
input [31:0] wr_line [1 << LINE_ADDR_LEN]
);
localparam RD_CYCLE = 50;
localparam WR_CYCLE = 50;
localparam LINE_SIZE = 1 << LINE_ADDR_LEN;
reg mem_wr_req = 1'b0;
reg [(ADDR_LEN + LINE_ADDR_LEN) - 1 : 0] mem_addr = 0;
reg [31:0] mem_wr_data = 0;
wire [31:0] mem_rd_data;
mem #(
.ADDR_LEN ( ADDR_LEN + LINE_ADDR_LEN )
) mem_inst (
.clk ( clk ),
.rst ( rst ),
.addr ( mem_addr ),
.rd_data ( mem_rd_data ),
.wr_req ( mem_wr_req ),
.wr_data ( mem_wr_data )
);
reg [31:0] rd_delay = 0, wr_delay = 0;
wire rd_ok = (rd_delay >= RD_CYCLE);
wire wr_ok = (wr_delay >= WR_CYCLE);
reg rd_cycle, wr_cycle;
reg [ADDR_LEN - 1:0] addr_last = 0;
reg [31:0] rd_line_latch [LINE_SIZE];
wire [LINE_ADDR_LEN - 1 : 0] wr_line_addr = wr_delay - (WR_CYCLE - LINE_SIZE);
wire [LINE_ADDR_LEN - 1 : 0] rd_line_addr = rd_delay - 1;
wire [LINE_ADDR_LEN - 1 : 0] rd_out_line_addr = rd_delay - 3;
assign gnt = (rd_cycle & rd_ok) | (wr_cycle & wr_ok);
always @ (posedge clk or posedge rst)
if(rst)
addr_last <= 0;
else
addr_last <= addr;
always @ (*) begin
rd_cycle = 1'b0;
wr_cycle = 1'b0;
if(addr_last == addr)
if(rd_req)
rd_cycle = 1'b1;
else if(wr_req)
wr_cycle = 1'b1;
end
always @ (posedge clk or posedge rst) // ?????
if(rst) begin
mem_wr_req = 1'b0;
mem_addr = 0;
mem_wr_data = 0;
end else begin
mem_wr_req = 1'b0;
mem_addr = 0;
mem_wr_data = 0;
if (wr_cycle) begin
rd_delay <= 0;
if(wr_ok) begin
wr_delay <= 0;
end else begin
if(wr_delay >= (WR_CYCLE - LINE_SIZE)) begin
mem_wr_req = 1'b1;
mem_addr = {addr, wr_line_addr};
mem_wr_data = wr_line[wr_line_addr];
end
wr_delay <= wr_delay + 1;
end
end else if(rd_cycle) begin
wr_delay <= 0;
if(rd_ok) begin
rd_line <= rd_line_latch;
end else begin
if(rd_delay >= 1 && rd_delay < 1 + LINE_SIZE) begin
mem_addr = {addr, rd_line_addr};
end
if(rd_delay >= 3 && rd_delay < 3 + LINE_SIZE) begin
rd_line_latch[rd_out_line_addr] <= mem_rd_data;
end
rd_delay <= rd_delay + 1;