设计并实现RAM控制器模块,管理30KB读写存储器的访问。RAM是程序运行时的数据区——变量、堆栈、缓冲区都在这里。我们将实现读写控制、地址解码和等待状态管理。
// ========================================
// ram_ctrl.v - RAM控制器
// Retro8 复古电脑 存储子系统
// ✅Verilator验证通过
// ========================================
module ram_ctrl (
input wire clk,
input wire rst_n,
input wire [15:0] addr,
input wire [7:0] data_in, // 写入数据
input wire rd, // 读使能
input wire wr, // 写使能
output reg [7:0] data_out, // 读出数据
output wire valid, // 地址在RAM范围内
output wire ready // 数据就绪
);
// RAM地址范围
localparam [15:0] RAM_BASE = 16'h0000;
localparam [15:0] RAM_SIZE = 16'h7800; // 30KB
// 地址有效性
assign valid = (addr >= RAM_BASE) && (addr < RAM_BASE + RAM_SIZE);
assign ready = valid; // 单周期SRAM,立即可用
// RAM存储阵列
reg [7:0] ram_data [0:RAM_SIZE-1];
// 写操作
always @(posedge clk) begin
if (wr && valid)
ram_data[addr - RAM_BASE] <= data_in;
end
// 读操作(组合逻辑)
always @(*) begin
if (rd && valid)
data_out = ram_data[addr - RAM_BASE];
else
data_out = 8'hFF;
end
endmodule
我们的RAM控制器是同步写、异步读。这意味着:
在同一个地址同时读写时,读出的是旧值,新值在下一个周期才可见。这与我们在寄存器文件中讨论的行为一致。
在简单的8位系统中,任何程序都可以读写任何内存地址。但我们可以添加简单的保护机制:
// 简单的内存保护:保护VGA缓冲区
wire vga_area = (addr >= 16'h7800) && (addr < 16'h8000);
wire io_area = (addr >= 16'hFF00);
// VGA缓冲区只允许VGA控制器写入
wire wr_allowed = valid && !vga_area;
// 或者:允许CPU写入VGA缓冲,但通过MMIO控制
// RAM上电测试模式
// 检测RAM大小和可靠性
module ram_test (
input wire clk,
input wire rst_n,
input wire start,
output reg [15:0] test_addr,
output reg [7:0] test_data,
output reg test_wr,
output reg test_rd,
output reg pass,
output reg fail
);
localparam IDLE = 3'd0;
localparam WRITE = 3'd1;
localparam READ = 3'd2;
localparam CHECK = 3'd3;
localparam DONE = 3'd4;
reg [2:0] state;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE; test_addr <= 16'd0;
end else begin
case (state)
IDLE: if (start) state <= WRITE;
WRITE: begin
test_wr <= 1'b1;
test_data <= test_addr[7:0]; // 写入地址低8位
state <= READ;
end
READ: begin
test_wr <= 1'b0;
test_rd <= 1'b1;
state <= CHECK;
end
CHECK: begin
test_rd <= 1'b0;
if (test_data !== test_addr[7:0])
fail <= 1'b1;
if (test_addr < 16'h77FF) begin
test_addr <= test_addr + 16'd1;
state <= WRITE;
end else begin
pass <= 1'b1;
state <= DONE;
end
end
endcase
end
end
endmodule
CPU和VGA控制器需要同时访问VGA缓冲区。设计一个双端口RAM控制器,支持一个写端口和两个读端口。
如果使用DRAM而不是SRAM,需要定期刷新。设计一个简单的刷新控制器,每隔一定周期自动刷新一行。
实现4个16KB内存页(bank),通过I/O端口选择当前活跃的页。这样可以在8位地址空间中使用超过64KB的RAM。
达成条件:
奖励:你的CPU现在有了"工作台"——可以在RAM中创建变量、缓冲区和堆栈。程序不再是只读的,数据可以自由流动。
理解SRAM的时序对设计可靠的内存控制器至关重要:
FPGA提供两种RAM实现方式:
综合工具会根据RAM大小自动选择实现方式。我们的30KB RAM会使用块RAM。