`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