- rtl/:12 个可综合的处理器源文件(顶层 cpu21_riscv_redirect_int_bpb.v,其余为各功能单元)。 - board/:板级顶层 cpu21_board_top.v、引脚约束 cpu21_board_top.xdc、片内 ROM 初始化 cpu21_board_rom.vh 与短延时版本 cpu21_board_rom_fast.vh。 - testbench/:保留三个测试平台;ROM 镜像统一放到 testbench/rom/,测试程序源码与期望值 仍在 testbench/programs/(其中的 .hex 是 RARS 原始输出,与 rom/ 里改过显示方式的版本 内容不同,两者都保留)。 - 同步更新:两个测试平台的 ROM 探测路径改为 testbench/rom/(并在头注释里补充说明: PowerShell 会把 -generic_top/--testplusarg 参数里的 "=" 拆开、Verilog 字符串中的 路径要用正斜杠);tools/hex2vh.py 的用法示例指向 board/cpu21_board_top.v; 说明文档新增“目录结构”一节并同步所有路径引用;.gitignore 补充 .Xil/。 - 清理:删除 *.hex.lst 中间文件与空的 .Xil/ 目录。 - 验证:isa_test 24/24、lui_test 4/4、mmio_test 5/5 三项回归通过;两个基线(5560 / 6003 周期)与整理前一致;板级仿真(默认 ROM 首次写入 LED=0001、快速 ROM 跑马灯循环)正常; Vivado 工程源文件路径同步到 rtl/ 与 board/ 后综合通过(LUT 5276、23 个引脚,与整理前 完全一致),因此已有 bit 文件仍然有效。
177 lines
7.0 KiB
Verilog
177 lines
7.0 KiB
Verilog
`timescale 1ns / 1ps
|
||
`default_nettype none
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// 上板顶层(Nexys4DDR,xc7a100tcsg324-1)
|
||
//
|
||
// 处理器顶层 cpu21_riscv_redirect_int_bpb 暴露了 775 位调试端口(各级 PC/IR、
|
||
// 计数器、BPB/中断观测等),直接作为实现顶层会因 I/O 超限而无法布局布线
|
||
// ([Place 30-415] IO Placement failed due to overutilization)。本模块只把
|
||
// 开发板真正需要的外设引出来,其余调试端口不连接,因此不占用引脚:
|
||
//
|
||
// clk100 (E3) -> CPU 时钟(100 MHz)
|
||
// rst_n (C12) -> 复位按键 CPU_RESETN,低有效,经复位同步器
|
||
// go (N17) -> 按键 BTNC:ecall 停机后按下继续运行
|
||
// irq_sw[2:0] -> 拨码开关 SW[2:0]:3 路中断源
|
||
// led[15:0] -> 板载 LED[15:0]:显示 MMIO(LED_ADDR)写入值的低 16 位
|
||
// led_we (R12) -> LED16_B:有 MMIO 写入时亮,并拉长约 0.17 s 便于观察
|
||
//
|
||
// 片内存储器:
|
||
// ROM:ROM_WORDS 字,组合读,内容由 `include "cpu21_board_rom.vh"` 给出
|
||
// (该文件由 tools/hex2vh.py 从 testbench/programs/*.hex 生成);
|
||
// RAM:RAM_WORDS 字,字节写使能 + 组合读;只响应地址 < 2^(RAM_AW+2),
|
||
// 因此 MMIO 地址不会落进 RAM(避免与 LED_ADDR 别名)。
|
||
//
|
||
// 注意:ROM_WORDS / RAM_WORDS 需为 2 的幂(默认 1024,对应地址位 [11:2])。
|
||
// -----------------------------------------------------------------------------
|
||
module cpu21_board_top #(
|
||
parameter [31:0] LED_ADDR = 32'h0000_1000,
|
||
parameter ROM_WORDS = 1024,
|
||
parameter RAM_WORDS = 1024,
|
||
parameter [31:0] USTATUS_INIT = 32'h0000_0001,
|
||
// 板载 100 MHz 分频给 CPU。ALU 里的除法/取余是组合电路(实测约 94 ns),
|
||
// 100 MHz 下无法收敛;默认 16 分频(6.25 MHz,周期 160 ns)留出足够余量。
|
||
parameter integer CLK_DIV = 16
|
||
) (
|
||
input wire clk100, // E3
|
||
input wire rst_n, // C12:CPU_RESETN,低有效
|
||
input wire go, // N17:BTNC
|
||
input wire [ 2:0] irq_sw, // SW[2:0]
|
||
output wire [15:0] led, // LED[15:0]
|
||
output wire led_we // LED16_B:MMIO 写入指示
|
||
);
|
||
localparam ROM_AW = $clog2(ROM_WORDS);
|
||
localparam RAM_AW = $clog2(RAM_WORDS);
|
||
|
||
wire arst = ~rst_n; // 异步复位(高有效)
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 时钟分频:100 MHz / CLK_DIV,经 BUFG 走全局时钟网络
|
||
// (XDC 中用 create_generated_clock 声明该分频时钟,否则时序分析会按
|
||
// 100 MHz 的 10 ns 要求去约束 CPU 内部路径而报大量违例)
|
||
// ---------------------------------------------------------------------------
|
||
reg [15:0] clk_cnt;
|
||
reg clk_div;
|
||
|
||
always @(posedge clk100 or posedge arst) begin
|
||
if (arst) begin
|
||
clk_cnt <= 16'd0;
|
||
clk_div <= 1'b0;
|
||
end else if (clk_cnt == (CLK_DIV / 2 - 1)) begin
|
||
clk_cnt <= 16'd0;
|
||
clk_div <= ~clk_div;
|
||
end else begin
|
||
clk_cnt <= clk_cnt + 16'd1;
|
||
end
|
||
end
|
||
|
||
// 不直接例化 BUFG 原语,保持 RTL 可跨工具仿真;
|
||
// 综合/实现时 Vivado 会为这条时钟网络自动插入 BUFG(全局时钟缓冲)。
|
||
wire clk_cpu = clk_div;
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 复位同步器(在 CPU 时钟域):按键异步置位、释放与 clk_cpu 同步
|
||
// ---------------------------------------------------------------------------
|
||
reg [2:0] rst_sync_q;
|
||
|
||
always @(posedge clk_cpu or posedge arst) begin
|
||
if (arst) rst_sync_q <= 3'b111;
|
||
else rst_sync_q <= {rst_sync_q[1:0], 1'b0};
|
||
end
|
||
|
||
wire reset = rst_sync_q[2];
|
||
|
||
// CPU 接口连线(先声明:后面的 ROM/RAM 逻辑与例化都要用到)
|
||
wire [31:0] instr_addr;
|
||
wire [31:0] data_addr;
|
||
wire [31:0] data_wdata;
|
||
wire [ 3:0] data_wstrb;
|
||
wire data_we;
|
||
wire [31:0] led_data;
|
||
wire led_valid;
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 指令 ROM:组合读
|
||
// ---------------------------------------------------------------------------
|
||
reg [31:0] rom [0:ROM_WORDS-1];
|
||
integer i;
|
||
|
||
initial begin
|
||
for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h0000_0013; // NOP 填充
|
||
`ifdef BOARD_ROM_FAST
|
||
// 仿真专用:把演示程序的延时改小的版本(综合流程不会用到)
|
||
`include "cpu21_board_rom_fast.vh"
|
||
`else
|
||
`include "cpu21_board_rom.vh"
|
||
`endif
|
||
end
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 数据 RAM:字节写使能 + 组合读
|
||
// ---------------------------------------------------------------------------
|
||
reg [31:0] ram[0:RAM_WORDS-1];
|
||
integer j;
|
||
|
||
initial begin
|
||
for (j = 0; j < RAM_WORDS; j = j + 1) ram[j] = 32'b0;
|
||
end
|
||
|
||
wire [ROM_AW+1:2] rom_index = instr_addr[ROM_AW+1:2];
|
||
wire [RAM_AW+1:2] ram_index = data_addr[RAM_AW+1:2];
|
||
wire ram_sel = (data_addr[31:RAM_AW+2] == 0);
|
||
|
||
wire [31:0] instr_word = rom[rom_index];
|
||
wire [31:0] data_rdata = ram_sel ? ram[ram_index] : 32'b0;
|
||
|
||
always @(posedge clk_cpu) begin
|
||
if (ram_sel && data_we) begin
|
||
if (data_wstrb[0]) ram[ram_index][7:0] <= data_wdata[7:0];
|
||
if (data_wstrb[1]) ram[ram_index][15:8] <= data_wdata[15:8];
|
||
if (data_wstrb[2]) ram[ram_index][23:16] <= data_wdata[23:16];
|
||
if (data_wstrb[3]) ram[ram_index][31:24] <= data_wdata[31:24];
|
||
end
|
||
end
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 处理器:只连接上板需要的端口,调试端口留空(不产生 I/O)
|
||
// ---------------------------------------------------------------------------
|
||
cpu21_riscv_redirect_int_bpb #(
|
||
.LED_ADDR (LED_ADDR),
|
||
.USTATUS_INIT(USTATUS_INIT)
|
||
) u_cpu (
|
||
.clk (clk_cpu),
|
||
.reset (reset),
|
||
.instr_i (instr_word),
|
||
.data_rdata_i(data_rdata),
|
||
.irq_i (irq_sw),
|
||
.go_i (go),
|
||
|
||
.instr_addr_o(instr_addr),
|
||
.data_addr_o (data_addr),
|
||
.data_wdata_o(data_wdata),
|
||
.data_wstrb_o(data_wstrb),
|
||
.data_we_o (data_we),
|
||
|
||
.led_data_o (led_data),
|
||
.led_valid_o(led_valid)
|
||
);
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 板载 LED:显示 MMIO 写入值的低 16 位;写入指示拉长便于观察
|
||
// ---------------------------------------------------------------------------
|
||
assign led = led_data[15:0];
|
||
|
||
reg [23:0] led_we_cnt;
|
||
|
||
always @(posedge clk_cpu or posedge arst) begin
|
||
if (arst) led_we_cnt <= 24'd0;
|
||
else if (led_valid)
|
||
led_we_cnt <= 24'hFF_FFFF; // 约 2.7 s @6.25 MHz(分频后的 CPU 时钟)
|
||
else if (led_we_cnt != 24'd0) led_we_cnt <= led_we_cnt - 24'd1;
|
||
end
|
||
|
||
assign led_we = (led_we_cnt != 24'd0);
|
||
endmodule
|
||
|
||
`default_nettype wire
|