1. Title
CPU RAM 설계
2. Category
"System Verilog", "BASYS3", "VIVADO"
3. Key Concepts
RAM in SystemVerilog
4. Setup
8 BIT DATA, 4 BIT ADDRESS를 갖는 간단한 동기식 RAM (Random Access Memory) 을 구현하고 TestBench를 진행한다.
RAM 기술 방식에 따른 RAM 모양의 차이를 이해한다.
5. Code Review
module RAM(
input logic clk,
input logic we,
input logic [3:0] addr,
input logic [7:0] W_data,
output logic [7:0] R_data
);
logic [8:0] mem[0:2**4-1];
always_ff @( posedge clk ) begin
if(we) begin
mem [addr] <= W_data;
end else begin
R_data <= mem[addr];
end
end
endmodule
// 동기식 읽기 + 쓰기, we=0일 때만 읽기 수행
module RAM(
input logic clk,
input logic we,
input logic [3:0] addr,
input logic [7:0] W_data,
output logic [7:0] R_data
);
logic [8:0] mem[0:2**4-1];
always_ff @( posedge clk ) begin
if(we) begin
mem [addr] <= W_data;
end
end
assign R_data = we ? 'z : mem[addr];
endmodule
// 비동기 읽기, we=1이면 high-Z('z') 출력
module RAM(
input logic clk,
input logic we,
input logic [3:0] addr,
input logic [7:0] W_data,
output logic [7:0] R_data
);
logic [8:0] mem[0:2**4-1];
always_ff @( posedge clk ) begin
if(we) begin
mem [addr] <= W_data;
end
end
assign R_data = mem[addr];
endmodule
// 비동기 읽기, we와 상관없이 R_data가 항상 mem[addr] 출력
module RAM(
input logic clk,
input logic we,
input logic [3:0] addr,
input logic [7:0] W_data,
output logic [7:0] R_data
);
logic [8:0] mem[0:2**4-1];
always_ff @( posedge clk ) begin
if(we) begin
mem [addr] <= W_data;
end
end
always_ff @( posedge clk ) begin
R_data <= mem[addr];
end
endmodule
// 동기식 읽기 + 쓰기, R_data가 한 클럭 늦게 반영됨
6. Testing and Debugging
* Testing Tools: VIVADO, Modelsim
`timescale 1ns / 1ps
interface RAM_interface ();
logic clk;
logic we;
logic [3:0] addr;
logic [7:0] W_data;
logic [7:0] R_data;
endinterface //RAM_interface
class transaction;
rand logic we;
rand logic [3:0] addr;
rand logic [7:0] W_data;
logic [7:0] R_data;
task display(string name);
$display("[%s] : we = %x | addr = %x | W_data = %x | R_data = %x ", name, we, addr, W_data, R_data);
endtask
endclass //transaction
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbx;
event scb_gen_event;
function new(mailbox #(transaction) gen2drv_mbx, event scb_gen_event);
this.gen2drv_mbx = gen2drv_mbx;
this.scb_gen_event = scb_gen_event;
endfunction //new()
task run(int run_time);
repeat(run_time) begin
trans = new();
trans.randomize();
gen2drv_mbx.put(trans);
trans.display("GEN");
@(scb_gen_event);
end
endtask
endclass //generator
class driver;
virtual RAM_interface RAM_interface;
transaction trans;
mailbox #(transaction) gen2drv_mbx;
event drv_mon_event;
function new(virtual RAM_interface RAM_interface, mailbox #(transaction)gen2drv_mbx, event drv_mon_event);
this.RAM_interface = RAM_interface;
this.gen2drv_mbx = gen2drv_mbx;
this.drv_mon_event = drv_mon_event;
endfunction //new()
task reset();
RAM_interface.we = 0;
RAM_interface.addr = 0;
RAM_interface.W_data = 0;
repeat (5) begin
@(posedge RAM_interface.clk);
end
for(int i = 0; i < 16 ; i++) begin
RAM_interface.we = 1;
RAM_interface.addr = i;
RAM_interface.W_data = 8'hff;
@(posedge RAM_interface.clk);
end
@(posedge RAM_interface.clk);
endtask
task run();
forever begin
#1; // race condition 방지.
// driver가 트랜잭션을 처리하고 #1만큼 대기하야만
// 이 대기 시간 동안 monitor가 drv_mon_event를 감지하고 트랜잭션을 처리할 수 있음.
gen2drv_mbx.get(trans);
RAM_interface.we = trans.we;
RAM_interface.addr = trans.addr;
RAM_interface.W_data = trans.W_data;
trans.display("DRV");
-> drv_mon_event;
end
endtask
endclass //driver
class monitor;
virtual RAM_interface RAM_interface;
transaction trans;
mailbox #(transaction) mon2scb_mbx;
event drv_mon_event;
function new(virtual RAM_interface RAM_interface, mailbox #(transaction) mon2scb_mbx, event drv_mon_event);
this.RAM_interface = RAM_interface;
this.mon2scb_mbx = mon2scb_mbx;
this.drv_mon_event = drv_mon_event;
endfunction //new()
task run();
forever begin
@(drv_mon_event);
trans = new();
if(RAM_interface.we) begin
trans.we = RAM_interface.we;
trans.addr = RAM_interface.addr;
trans.W_data = RAM_interface.W_data;
@(posedge RAM_interface.clk);
#1;
end else begin
#1;
trans.we = RAM_interface.we;
trans.addr = RAM_interface.addr;
trans.R_data = RAM_interface.R_data;
end
mon2scb_mbx.put(trans);
trans.display("MON");
end
endtask
endclass //monitor
class scoreboard;
transaction trans;
mailbox #(transaction) mon2scb_mbx;
event scb_gen_event;
int total_count, pass_count, fail_count;
int read_count, write_count;
byte mem[16];
byte read_data;
function new(mailbox #(transaction) mon2scb_mbx, event scb_gen_event);
this.mon2scb_mbx = mon2scb_mbx;
this.scb_gen_event = scb_gen_event;
total_count = 0;
pass_count = 0;
fail_count = 0;
read_count = 0;
write_count = 0;
for(int i = 0; i < 16; i++) begin
mem[i] = 8'hff;
end
endfunction //new()
task run();
forever begin
mon2scb_mbx.get(trans);
trans.display("SCB");
if(trans.we) begin
mem[trans.addr] = trans.W_data;
$display("Write!!");
write_count++;
end else begin
read_data = mem[trans.addr];
if(read_data == trans.R_data) begin
$display("PASS!!! read data: %x == trans.R_data: %x", read_data, trans.R_data);
pass_count++;
end else begin
$display("FAIL!!! read data: %x == trans.R_data: %x", read_data, trans.R_data);
fail_count++;
end
read_count++;
end
total_count++;
$display("");
-> scb_gen_event;
end
endtask
endclass //scoreboard
class environment;
virtual RAM_interface RAM_interface;
transaction trans;
generator generator;
driver driver;
monitor monitor;
scoreboard scoreboard;
event scb_gen_event, drv_mon_event;
mailbox #(transaction) gen2drv_mbx, mon2scb_mbx;
function new(virtual RAM_interface RAM_interface);
gen2drv_mbx = new();
mon2scb_mbx = new();
generator = new(gen2drv_mbx, scb_gen_event);
driver = new(RAM_interface, gen2drv_mbx, drv_mon_event);
monitor = new(RAM_interface, mon2scb_mbx, drv_mon_event);
scoreboard = new(mon2scb_mbx, scb_gen_event);
endfunction //new()
task report();
$display("===============================================");
$display("=============== FINAL REPORT =================");
$display(" TOTAL TEST : %d ", scoreboard.total_count);
$display(" WRITE TEST : %d ", scoreboard.write_count);
$display(" READ TEST : %d ", scoreboard.read_count);
$display(" PASS TEST : %d ", scoreboard.pass_count);
$display(" FAIL TEST : %d ", scoreboard.fail_count);
$display("===============================================");
$display("===============================================");
$display("===============================================");
#10
$finish;
endtask
task pre_run();
driver.reset();
endtask
task run();
fork
generator.run(10000);
driver.run();
monitor.run();
scoreboard.run();
join_any
report();
#10;
$finish;
endtask
endclass //environment
module tb_RAM(
);
RAM_interface RAM_interface();
environment env;
RAM DUT(
.clk(RAM_interface.clk),
.we(RAM_interface.we),
.addr(RAM_interface.addr),
.W_data(RAM_interface.W_data),
.R_data(RAM_interface.R_data)
);
always
#5 RAM_interface.clk = ~RAM_interface.clk;
initial begin
RAM_interface.clk = 0;
env = new(RAM_interface);
env.pre_run();
env.run();
end
endmodule
'AI SOC COURSE > SYSTEM VERILOG (CPU 설계)' 카테고리의 다른 글
CPU Course Review - 10. Analysis of Assembly Language (Single Cycle RISC-V) (0) | 2025.02.21 |
---|---|
CPU Course Review - 09. RISC-V CPU 개요 (0) | 2025.02.12 |
CPU Course Review - 07. Register (0) | 2025.02.11 |
CPU Course Review - 06. TestBench Revision (0) | 2025.02.11 |
CPU Course Review - 05. Accumulator (0 ~ 9) (0) | 2025.02.10 |