立足在线FPGA设计仿真平台StepFPGA、MakerChip与CPUlator,借助人工智能AIIMA-Copilot-DS与MuleRun工具,使用Verilog\SystemVerilog语言,展开了一系列的典型综合逻辑电路编码设计仿真及其虚拟动态实景与波形展现的FPGA_SoC项目:简易八位机、主流RISC-V、五级流水线RISC-V、有限数字滤波FIR、Nios交互指示、RISC-V中断驱动。

1 StepFPGA--简易八位机SoC
1.1 方案规划
简易总线架构 + 定时器中断 + PWM输出,驱动LED呼吸灯效果:设计一个带简易总线的SoC系统,包含8位软核处理器、定时器外设(支持中断)、PWM输出外设和总线译码器。处理器通过总线配置定时器和PWM参数,实现LED呼吸灯效果。
|-----------|--------------|-------------------------|
| 址范围 | 外设 | 说明 |
| 0x00-0x7F | 数据RAM | 128字节通用数据 |
| 0x80 | TIMER_CTRL | 定时器控制 0使能 1中断使能 |
| 0x81 | TIMER_PRESET | 定时器预设值 (计数上限) |
| 0x82 | TIMER_COUNT | 定时器当前值 (只读) |
| 0x83 | TIMER_STATUS | 中断标志 0溢出 (写1清除) |
| 0x90 | PWM_CTRL | PWM控制 0使能 |
| 0x91 | PWM_DUTY | PWM占空比 (0-255) |
|--------------|---------------|
| 模块 | 功能 |
| simple_cpu | 8位寄存器型软核处理器 |
| bus_decoder | 地址译码与总线仲裁 |
| timer_periph | 8位定时器+中断 |
| pwm_periph | 8位PWM输出 |
| data_ram | 128×8bit数据RAM |
| pwm_soc_top | 顶层集成 |

1.2 编码实现
1 simple_cpu.v
cpp
// ============================================
// simple_cpu.v --- 8位寄存器型软核处理器: 4个通用寄存器 R0-R3, 总线接口
// ============================================
module simple_cpu (
input clk,
input rst_n,
input irq, // 中断请求
output reg [7:0] bus_addr, // 总线接口
output reg [7:0] bus_wdata,
input [7:0] bus_rdata,
output reg bus_rd,
output reg bus_wr
);
// 指令格式: [15:12]OP [11:10]Rd [9:8]Rs [7:0]Imm
localparam OP_NOP = 4'h0,
OP_LDI = 4'h1, // Rd = Imm
OP_LD = 4'h2, // Rd = MEM[Imm]
OP_ST = 4'h3, // MEM[Imm] = Rd
OP_ADD = 4'h4, // Rd = Rd + Rs
OP_SUB = 4'h5, // Rd = Rd - Rs
OP_JMP = 4'h6, // PC = Imm
OP_JNZ = 4'h7, // if Rd!=0: PC = Imm
OP_ADDI = 4'h8, // Rd = Rd + Imm
OP_SUBI = 4'h9, // Rd = Rd - Imm
OP_RETI = 4'hA; // 中断返回
localparam S_FETCH = 3'd0,
S_DECODE = 3'd1,
S_EXECUTE = 3'd2,
S_MEMRD = 3'd3,
S_MEMWR = 3'd4;
reg [2:0] state;
reg [7:0] pc;
reg [7:0] regs [0:3]; // R0-R3
reg [15:0] ir;
reg [7:0] saved_pc; // 中断保存PC
reg in_irq; // 中断中标志
wire [3:0] opcode = ir[15:12];
wire [1:0] rd_sel = ir[11:10];
wire [1:0] rs_sel = ir[9:8];
wire [7:0] imm = ir[7:0];
// 指令ROM (内嵌程序)
reg [15:0] prog_rom [0:63];
initial begin
// 主程序: 配置Timer和PWM, 循环递增占空比
// 0: LDI R0, 0x03 ; Timer使能+中断使能
prog_rom[0] = 16'h1003;
// 1: ST R0, 0x80 ; 写TIMER_CTRL
prog_rom[1] = 16'h3080;
// 2: LDI R0, 0xFF ; 定时器预设值255
prog_rom[2] = 16'h10FF;
// 3: ST R0, 0x81 ; 写TIMER_PRESET
prog_rom[3] = 16'h3081;
// 4: LDI R0, 0x01 ; PWM使能
prog_rom[4] = 16'h1001;
// 5: ST R0, 0x90 ; 写PWM_CTRL
prog_rom[5] = 16'h3090;
// 6: LDI R1, 0x00 ; R1 = 占空比初始值
prog_rom[6] = 16'h1400;
// 7: ST R1, 0x91 ; 写PWM_DUTY = R1
prog_rom[7] = 16'h3491;
// 8: ADDI R1, 0x10 ; R1 += 16 (递增占空比)
prog_rom[8] = 16'h8410;
// 9: JMP 0x07 ; 循环写PWM
prog_rom[9] = 16'h6007;
// ISR入口 (地址 0x30): 清除Timer中断标志
// 48: LDI R2, 0x01
prog_rom[48] = 16'h1801;
// 49: ST R2, 0x83 ; 写TIMER_STATUS清标志
prog_rom[49] = 16'h3883;
// 50: RETI
prog_rom[50] = 16'hA000;
end
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= S_FETCH;
pc <= 8'd0;
ir <= 16'd0;
bus_rd <= 0;
bus_wr <= 0;
in_irq <= 0;
saved_pc <= 0;
for (i = 0; i < 4; i = i + 1) regs[i] <= 8'd0;
end else begin
bus_rd <= 0;
bus_wr <= 0;
case (state)
S_FETCH: begin
// 中断检测 (仅在非中断状态)
if (irq && !in_irq) begin
saved_pc <= pc;
pc <= 8'h30; // ISR入口
in_irq <= 1;
end
ir <= prog_rom[pc[5:0]];
state <= S_DECODE;
end
S_DECODE: begin
state <= S_EXECUTE;
end
S_EXECUTE: begin
case (opcode)
OP_NOP: ;
OP_LDI: regs[rd_sel] <= imm;
OP_LD: begin
bus_addr <= imm;
bus_rd <= 1;
state <= S_MEMRD;
end
OP_ST: begin
bus_addr <= imm;
bus_wdata <= regs[rd_sel];
bus_wr <= 1;
state <= S_MEMWR;
end
OP_ADD: regs[rd_sel] <= regs[rd_sel] + regs[rs_sel];
OP_SUB: regs[rd_sel] <= regs[rd_sel] - regs[rs_sel];
OP_ADDI: regs[rd_sel] <= regs[rd_sel] + imm;
OP_SUBI: regs[rd_sel] <= regs[rd_sel] - imm;
OP_JMP: pc <= imm;
OP_JNZ: if (regs[rd_sel] != 0) pc <= imm;
OP_RETI: begin
pc <= saved_pc;
in_irq <= 0;
end
endcase
if (opcode != OP_LD && opcode != OP_ST) begin
if (opcode != OP_JMP &&
!(opcode == OP_JNZ && regs[rd_sel] != 0) &&
opcode != OP_RETI)
pc <= pc + 1;
state <= S_FETCH;
end
end
S_MEMRD: begin
regs[rd_sel] <= bus_rdata;
pc <= pc + 1;
state <= S_FETCH;
end
S_MEMWR: begin
pc <= pc + 1;
state <= S_FETCH;
end
endcase
end
end
endmodule
2 bus_coder.v
cpp
// ============================================
// bus_decoder.v --- 地址译码器
// ============================================
module bus_decoder (
input [7:0] addr,
input [7:0] wdata,
input rd,
input wr,
output reg [7:0] rdata,
// RAM
output ram_cs,
input [7:0] ram_rdata,
// Timer
output timer_cs,
input [7:0] timer_rdata,
// PWM
output pwm_cs,
input [7:0] pwm_rdata
);
// 地址译码
assign ram_cs = (addr[7] == 1'b0); // 0x00-0x7F
assign timer_cs = (addr[7:4] == 4'h8); // 0x80-0x8F
assign pwm_cs = (addr[7:4] == 4'h9); // 0x90-0x9F
// 读数据多路选择
always @(*) begin
case (1'b1)
ram_cs: rdata = ram_rdata;
timer_cs: rdata = timer_rdata;
pwm_cs: rdata = pwm_rdata;
default: rdata = 8'h00;
endcase
end
endmodule
3 temer_periph.v
cpp
// ============================================
// timer_periph.v --- 8位定时器外设 (带中断), 寄存器: CTRL(+0) PRESET(+1) COUNT(+2) STATUS(+3)
// ============================================
module timer_periph (
input clk,
input rst_n,
input cs, // 片选
input [1:0] addr, // 寄存器偏移
input [7:0] wdata,
output reg [7:0] rdata,
input rd,
input wr,
output irq // 中断输出
);
reg [7:0] ctrl; // [0]使能 [1]中断使能
reg [7:0] preset; // 预设计数值
reg [7:0] counter; // 当前计数值
reg ovf_flag; // 溢出标志
wire timer_en = ctrl[0];
wire irq_en = ctrl[1];
assign irq = ovf_flag & irq_en;
// 寄存器写
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ctrl <= 8'd0;
preset <= 8'hFF;
counter <= 8'd0;
ovf_flag <= 0;
end else begin
// 写寄存器
if (cs && wr) begin
case (addr)
2'd0: ctrl <= wdata;
2'd1: preset <= wdata;
2'd3: ovf_flag <= ovf_flag & ~wdata[0]; // 写1清除
endcase
end
// 计数器逻辑
if (timer_en) begin
if (counter >= preset) begin
counter <= 8'd0;
ovf_flag <= 1'b1;
end else
counter <= counter + 8'd1;
end
end
end
// 寄存器读
always @(*) begin
rdata = 8'd0;
if (cs && rd) begin
case (addr)
2'd0: rdata = ctrl;
2'd1: rdata = preset;
2'd2: rdata = counter;
2'd3: rdata = {7'd0, ovf_flag};
endcase
end
end
endmodule
4 pwm_periph.v
cpp
// ============================================
// pwm_periph.v --- 8位PWM输出外设, 寄存器: CTRL(+0) DUTY(+1)
// ============================================
module pwm_periph (
input clk,
input rst_n,
input cs,
input [0:0] addr, // 1位偏移
input [7:0] wdata,
output reg [7:0] rdata,
input rd,
input wr,
output pwm_out // PWM输出
);
reg [7:0] ctrl; // [0]使能
reg [7:0] duty; // 占空比 (0=全灭, 255=全亮)
reg [7:0] counter; // 自由运行计数器
wire pwm_en = ctrl[0];
assign pwm_out = pwm_en && (counter < duty);
// PWM计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
ctrl <= 8'd0;
duty <= 8'd0;
counter <= 8'd0;
end else begin
counter <= counter + 8'd1; // 自由运行,自然溢出
if (cs && wr) begin
case (addr)
1'd0: ctrl <= wdata;
1'd1: duty <= wdata;
endcase
end
end
end
// 读寄存器
always @(*) begin
rdata = 8'd0;
if (cs && rd) begin
case (addr)
1'd0: rdata = ctrl;
1'd1: rdata = duty;
endcase
end
end
endmodule
5 data_ram.v
cpp
// ============================================
// data_ram.v --- 128×8bit数据RAM
// ============================================
module data_ram (
input clk,
input cs,
input [6:0] addr,
input [7:0] wdata,
output reg [7:0] rdata,
input rd,
input wr
);
reg [7:0] mem [0:127];
always @(posedge clk) begin
if (cs && wr)
mem[addr] <= wdata;
end
always @(*) begin
rdata = 8'd0;
if (cs && rd)
rdata = mem[addr];
end
endmodule
6 pwm_soc_top.v
cpp
// ============================================
// pwm_soc_top.v --- Timer/PWM SoC顶层
// ============================================
module pwm_soc_top (
input clk_12m,
input rst_n,
output pwm_led // PWM驱动LED
);
wire [7:0] bus_addr, bus_wdata, bus_rdata;
wire bus_rd, bus_wr;
wire irq;
// 外设片选
wire ram_cs, timer_cs, pwm_cs;
wire [7:0] ram_rdata, timer_rdata, pwm_rdata;
simple_cpu u_cpu (
.clk(clk_12m), .rst_n(rst_n), .irq(irq),
.bus_addr(bus_addr), .bus_wdata(bus_wdata),
.bus_rdata(bus_rdata), .bus_rd(bus_rd), .bus_wr(bus_wr)
);
bus_decoder u_bus (
.addr(bus_addr), .wdata(bus_wdata), .rd(bus_rd), .wr(bus_wr),
.rdata(bus_rdata),
.ram_cs(ram_cs), .ram_rdata(ram_rdata),
.timer_cs(timer_cs), .timer_rdata(timer_rdata),
.pwm_cs(pwm_cs), .pwm_rdata(pwm_rdata)
);
data_ram u_ram (
.clk(clk_12m), .cs(ram_cs),
.addr(bus_addr[6:0]),
.wdata(bus_wdata), .rdata(ram_rdata),
.rd(bus_rd), .wr(bus_wr)
);
timer_periph u_timer (
.clk(clk_12m), .rst_n(rst_n), .cs(timer_cs),
.addr(bus_addr[1:0]),
.wdata(bus_wdata), .rdata(timer_rdata),
.rd(bus_rd), .wr(bus_wr), .irq(irq)
);
pwm_periph u_pwm (
.clk(clk_12m), .rst_n(rst_n), .cs(pwm_cs),
.addr(bus_addr[0]),
.wdata(bus_wdata), .rdata(pwm_rdata),
.rd(bus_rd), .wr(bus_wr), .pwm_out(pwm_led)
);
endmodule
1.3 仿真测试
1 测试文件pwm_soc_tb.v
cpp
// ============================================
// pwm_soc_tb.v --- Timer/PWM SoC仿真
// ============================================
`timescale 1ns / 1ps
module pwm_soc_tb;
reg clk;
reg rst_n;
wire pwm_led;
pwm_soc_top uut (
.clk_12m (clk),
.rst_n (rst_n),
.pwm_led (pwm_led)
);
// 12MHz 时钟
initial clk = 0;
always #41.67 clk = ~clk;
initial begin
$dumpfile("pwm_wave.vcd");
$dumpvars(0, pwm_soc_tb);
rst_n = 0;
#300;
rst_n = 1;
// 运行足够长观察多次PWM占空比变化
// PWM周期 = 256 clk = 21.3μs @12MHz
// 程序循环周期 ≈ 9条指令×3~4clk ≈ 30clk
#200_000; // 200μs
$display("=== PWM SoC Simulation Complete ===");
$finish;
end
// 监控PWM占空比变化
always @(uut.u_pwm.duty) begin
$display("[%0t] PWM Duty changed to: %d (%.1f%%)",
$time, uut.u_pwm.duty,
uut.u_pwm.duty * 100.0 / 255.0);
end
// 监控定时器中断
always @(posedge uut.u_timer.irq)
$display("[%0t] *** TIMER IRQ fired! counter=%d ***",
$time, uut.u_timer.counter);
// 监控CPU指令执行
always @(posedge clk) begin
if (rst_n && uut.u_cpu.state == 3'd2)
$display("[%0t] CPU: PC=%02h OP=%01h R0=%02h R1=%02h",
$time, uut.u_cpu.pc,
uut.u_cpu.opcode,
uut.u_cpu.regs[0], uut.u_cpu.regs[1]);
end
endmodule
2 仿真波形
仿真运行步骤
A.创建项目:新建项目 "pwm_soc_demo",选择 EG4S20 芯片型号
B.添加6个设计文件:创建 simple_cpu.v、bus_decoder.v、timer_periph.v、pwm_periph.v、data_ram.v、pwm_soc_top.v
C.添加测试平台:创建pwm_soc_tb.v,仿真时间设为200μs以上
D.运行仿真:添加信号: clk, rst_n, bus_addr, bus_wdata, bus_wr, irq, pwm_out, duty, counter。可将 pwm_out 设为模拟显示模式观察占空比。
E.波形验证:观察 PWM duty 值从 0x00 递增 (每次+0x10): 0x00→0x10→0x20→...→0xF0→0x00(溢出)循环。PWM输出方波的高电平宽度应随之增加


展开一系列过程:逻辑综合à管脚分配àGPGA映射à文件下载。其中管脚分配过程如下图所示。在线系统没有EG4S20,这里以LCMXO2-4000HC替代,以演示相应进程。

2 MarkChip---FIR滤波器SoC
2.1 设计规划

|-----------------|------------------------|----------------------------------|
| 模块 | 功能 | 关键设计点 |
| fir_filter_8tap | 8阶FIR低通滤波器 | 16-bit数据/系数,40-bit累加器,组合MAC树 |
| regfile | 配置寄存器文件 | 8个寄存器,组合读/时序写,自清零位 |
| dma_ctrl | DMA数据搬运控制器 | 6态FSM,读→喂入FIR→写回 |
| axi_lite_slave | AXI-Lite从机总线接口 | 写通道3态FSM + 读通道2态 FSM |
| dsp_soc | SoC顶层集成 | AXI + test port 双路径 mux,1KB BRAM |
| top | MakerChip封装+ testbench | 复位同步,周期计数器,pass/fail 判定 |
2.2 编码实现
1 FIR 滤波器-- 直接型MAC 结构

关键实现:
- 系数通过case(ca)逐地址写入8个独立寄存器(避免RAM推断)
- 延迟线用展开赋值x0<=din; x1<=x0; ... 而非 for 循环(避免Verilator警告)
- MAC 用组合逻辑assign m = h0*x0 + ... + h7*x7(避免for循环累加的非阻塞赋值错误)
2 寄存器文件-- 内存映射

关键设计:
- 组合逻辑读:always @(*)实现零等待读
- 时序写:always @(posedge clk)单块内处理,无部分赋值冲突
- 自清零:cwe在写后下一个周期自动归零
3 DMA 控制器--6 态有限状态机

每个样本经历 5 个周期:读存储器 → 等待数据 → 喂入 FIR → 写回 → 循环。
4 AXI-Lite Slave-- 独立读写通道

关键特性:
- 支持 AW+W 同步到达(一个周期完成握手)
- 也支持 AW 先于 W 的分步握手
- 地址只取低 8 位(匹配寄存器文件地址空间)
5 程序编码

cpp
// ============================================================================
// DSP SoC: 8-tap FIR + DMA + AXI-Lite --- MakerChip Compatible
// ============================================================================
/* verilator lint_off TIMESCALEMOD */
/* verilator lint_off UNUSEDSIGNAL */
/* verilator lint_off UNOPTFLAT */
/* verilator lint_off BLKANDNBLK */
/* verilator lint_off WIDTH */
/* verilator lint_off PINCONNECTEMPTY */
/* verilator lint_off DECLFILENAME */
/* verilator lint_off INITIALDLY */
/* verilator lint_off CASEINCOMPLETE */
/* verilator lint_off UNSIGNED */
/* verilator lint_off CASEOVERLAP */
`timescale 1ns/1ps
// ============================================================================
// 1. 8-TAP FIR FILTER
// ============================================================================
module fir_filter_8tap (
input logic clk,
input logic rst_n,
input logic en,
input logic dv_in,
input logic [15:0] din,
input logic [2:0] ca,
input logic [15:0] cd,
input logic cwe,
output logic dv_out,
output logic [39:0] dout
);
logic signed [15:0] h0,h1,h2,h3,h4,h5,h6,h7;
logic signed [15:0] x0,x1,x2,x3,x4,x5,x6,x7;
logic signed [39:0] m;
always @(posedge clk)
if (cwe)
case (ca)
0: h0<=cd; 1: h1<=cd; 2: h2<=cd; 3: h3<=cd;
4: h4<=cd; 5: h5<=cd; 6: h6<=cd; 7: h7<=cd;
endcase
always @(posedge clk)
if (!rst_n) begin
x0<=0; x1<=0; x2<=0; x3<=0; x4<=0; x5<=0; x6<=0; x7<=0;
end else if (en && dv_in) begin
x0<=din; x1<=x0; x2<=x1; x3<=x2;
x4<=x3; x5<=x4; x6<=x5; x7<=x6;
end
assign m = h0*x0 + h1*x1 + h2*x2 + h3*x3
+ h4*x4 + h5*x5 + h6*x6 + h7*x7;
always @(posedge clk)
if (!rst_n) begin
dout <= 0;
dv_out <= 0;
end else begin
dv_out <= 0;
if (en && dv_in) begin
dout <= m;
dv_out <= 1;
end
end
endmodule
// ============================================================================
// 2. REGISTER FILE
// ============================================================================
module regfile (
input logic clk,
input logic rst_n,
input logic wr,
input logic [7:0] addr,
input logic [31:0] wd,
output logic [31:0] rd,
output logic [31:0] dma_src,
output logic [31:0] dma_dst,
output logic [15:0] dma_len,
output logic fir_en,
output logic dma_start,
output logic [2:0] ca,
output logic [15:0] cd,
output logic cwe,
output logic irq_en,
output logic irq_out,
input logic dma_done,
input logic dma_busy,
input logic fir_done
);
logic [31:0] r_ctrl, r_irq, r_dma_src, r_dma_dst;
logic [15:0] r_dma_len;
assign dma_src = r_dma_src;
assign dma_dst = r_dma_dst;
assign dma_len = r_dma_len;
assign fir_en = r_ctrl[0];
assign dma_start = r_ctrl[1];
assign irq_en = r_irq[1];
always @(*) begin
rd = 32'h0;
case (addr)
8'h00: rd = r_ctrl;
8'h04: rd = {28'b0, dma_busy, fir_done, dma_done, r_ctrl[0]};
8'h08: rd = r_irq;
8'h10: rd = r_dma_src;
8'h14: rd = r_dma_dst;
8'h18: rd = {16'b0, r_dma_len};
8'h20: rd = {29'b0, ca};
8'h24: rd = {16'b0, cd};
endcase
end
always @(posedge clk)
if (!rst_n) begin
r_ctrl <= 0;
r_irq <= 0;
r_dma_src <= 0;
r_dma_dst <= 0;
r_dma_len <= 0;
ca <= 0;
cd <= 0;
cwe <= 0;
irq_out <= 0;
end else begin
cwe <= 0;
if (wr) begin
case (addr)
8'h00: r_ctrl <= wd;
8'h08: r_irq <= wd;
8'h10: r_dma_src <= wd;
8'h14: r_dma_dst <= wd;
8'h18: r_dma_len <= wd[15:0];
8'h20: ca <= wd[2:0];
8'h24: begin cd <= wd[15:0]; cwe <= 1; end
endcase
end
if ((dma_done || fir_done) && r_irq[1])
irq_out <= 1;
else if (r_irq[0])
irq_out <= 0;
end
endmodule
// ============================================================================
// 3. DMA CONTROLLER
// ============================================================================
module dma_ctrl (
input logic clk,
input logic rst_n,
input logic start,
output logic busy,
output logic done,
input logic [31:0] cfg_src,
input logic [31:0] cfg_dst,
input logic [15:0] cfg_len,
output logic mem_re,
output logic [31:0] mem_ra,
input logic [15:0] mem_rd,
input logic mem_rv,
output logic mem_we,
output logic [31:0] mem_wa,
output logic [15:0] mem_wd,
output logic fir_vld,
output logic [15:0] fir_dat,
input logic fir_rdy
);
logic [2:0] s;
logic [15:0] cnt;
logic [31:0] sp, dp;
logic [15:0] db;
always @(posedge clk)
if (!rst_n) begin
s<=0; busy<=0; done<=0; cnt<=0; sp<=0; dp<=0; db<=0;
mem_re<=0; mem_ra<=0; mem_we<=0; mem_wa<=0; mem_wd<=0;
fir_vld<=0; fir_dat<=0;
end else begin
done<=0; mem_re<=0; mem_we<=0; fir_vld<=0;
case (s)
0: if (start) begin
sp<=cfg_src; dp<=cfg_dst; cnt<=cfg_len;
busy<=1; s<=1;
end else busy<=0;
1: if (cnt==0) s<=5;
else begin mem_re<=1; mem_ra<=sp; s<=2; end
2: if (mem_rv) begin db<=mem_rd; s<=3; end
3: begin fir_vld<=1; fir_dat<=db;
if (fir_rdy) s<=4; end
4: begin mem_we<=1; mem_wa<=dp; mem_wd<=db;
sp<=sp+2; dp<=dp+2; cnt<=cnt-1; s<=1; end
5: begin done<=1; busy<=0; s<=0; end
endcase
end
endmodule
// ============================================================================
// 4. AXI-LITE SLAVE
// ============================================================================
module axi_lite_slave (
input logic clk,
input logic rst_n,
// Write address channel
input logic [31:0] awaddr,
input logic awvalid,
output logic awready,
// Write data channel
input logic [31:0] wdata,
input logic [3:0] wstrb,
input logic wvalid,
output logic wready,
// Write response channel
output logic [1:0] bresp,
output logic bvalid,
input logic bready,
// Read address channel
input logic [31:0] araddr,
input logic arvalid,
output logic arready,
// Read data channel
output logic [31:0] rdata,
output logic [1:0] rresp,
output logic rvalid,
input logic rready,
// Internal register interface
output logic o_wr_en,
output logic [7:0] o_wr_addr,
output logic [31:0] o_wr_data,
output logic o_rd_en,
output logic [7:0] o_rd_addr,
input logic [31:0] i_rd_data
);
// Write channel FSM
logic [1:0] ws;
localparam WS_IDLE=0, WS_W=1, WS_B=2;
logic [7:0] wa_lat;
always @(posedge clk)
if (!rst_n) begin
ws <= WS_IDLE;
awready <= 0;
wready <= 0;
bvalid <= 0;
bresp <= 2'b00;
o_wr_en <= 0;
o_wr_addr <= 0;
o_wr_data <= 0;
wa_lat <= 0;
end else begin
o_wr_en <= 0;
case (ws)
WS_IDLE: begin
awready <= 1;
wready <= 1;
if (awvalid && wvalid) begin
wa_lat <= awaddr[7:0];
o_wr_addr <= awaddr[7:0];
o_wr_data <= wdata;
o_wr_en <= 1;
awready <= 0;
wready <= 0;
ws <= WS_B;
end else if (awvalid) begin
wa_lat <= awaddr[7:0];
awready <= 0;
ws <= WS_W;
end else if (wvalid) begin
o_wr_data <= wdata;
o_wr_en <= 1;
wready <= 0;
ws <= WS_B;
end
end
WS_W: begin
wready <= 1;
if (wvalid) begin
o_wr_addr <= wa_lat;
o_wr_data <= wdata;
o_wr_en <= 1;
wready <= 0;
ws <= WS_B;
end
end
WS_B: begin
bvalid <= 1;
bresp <= 2'b00;
if (bready) begin
bvalid <= 0;
ws <= WS_IDLE;
end
end
endcase
end
// Read channel FSM
logic [1:0] rs;
localparam RS_IDLE=0, RS_R=1;
always @(posedge clk)
if (!rst_n) begin
rs <= RS_IDLE;
arready <= 0;
rvalid <= 0;
rdata <= 0;
rresp <= 2'b00;
o_rd_en <= 0;
o_rd_addr <= 0;
end else begin
o_rd_en <= 0;
case (rs)
RS_IDLE: begin
arready <= 1;
if (arvalid) begin
o_rd_addr <= araddr[7:0];
o_rd_en <= 1;
arready <= 0;
rs <= RS_R;
end
end
RS_R: begin
rdata <= i_rd_data;
rresp <= 2'b00;
rvalid <= 1;
if (rready) begin
rvalid <= 0;
rs <= RS_IDLE;
end
end
endcase
end
endmodule
// ============================================================================
// 5. DSP SOC TOP
// ============================================================================
module dsp_soc (
input logic clk,
input logic rst_n,
// AXI-Lite slave port
input logic [31:0] s_awaddr,
input logic s_awvalid,
output logic s_awready,
input logic [31:0] s_wdata,
input logic [3:0] s_wstrb,
input logic s_wvalid,
output logic s_wready,
output logic [1:0] s_bresp,
output logic s_bvalid,
input logic s_bready,
input logic [31:0] s_araddr,
input logic s_arvalid,
output logic s_arready,
output logic [31:0] s_rdata,
output logic [1:0] s_rresp,
output logic s_rvalid,
input logic s_rready,
// Test port (bypasses AXI for direct register access)
input logic test_wr,
input logic [7:0] test_addr,
input logic [31:0] test_wd,
output logic [31:0] test_rd,
output logic irq
);
// AXI slave outputs
logic axi_wr_en;
logic [7:0] axi_wr_addr;
logic [31:0] axi_wr_data;
logic axi_rd_en;
logic [7:0] axi_rd_addr;
// Mux: test port has priority over AXI
logic rf_wr;
logic [7:0] rf_addr;
logic [31:0] rf_wd;
assign rf_wr = test_wr | axi_wr_en;
assign rf_addr = test_wr ? test_addr : axi_wr_addr;
assign rf_wd = test_wr ? test_wd : axi_wr_data;
// Internal wires
logic fir_en, dma_start, irq_en;
logic dma_done, dma_busy, fir_done;
logic [31:0] dma_src, dma_dst;
logic [15:0] dma_len;
logic [2:0] ca;
logic [15:0] cd;
logic cwe;
logic irq_out;
logic dma_mre;
logic [31:0] dma_mra;
logic [15:0] dma_mrd;
logic dma_mrv;
logic dma_mwe;
logic [31:0] dma_mwa;
logic [15:0] dma_mwd;
logic fir_vld;
logic [15:0] fir_dat;
logic [31:0] rf_rd;
// BRAM 1KB
logic [15:0] bram [0:511];
wire [8:0] ri = dma_mra[9:1];
wire [8:0] wi = dma_mwa[9:1];
always @(posedge clk)
if (dma_mwe && wi < 512) bram[wi] <= dma_mwd;
assign dma_mrd = bram[ri];
assign dma_mrv = dma_mre;
// AXI-Lite slave
axi_lite_slave u_axi (
.clk(clk), .rst_n(rst_n),
.awaddr(s_awaddr), .awvalid(s_awvalid), .awready(s_awready),
.wdata(s_wdata), .wstrb(s_wstrb), .wvalid(s_wvalid), .wready(s_wready),
.bresp(s_bresp), .bvalid(s_bvalid), .bready(s_bready),
.araddr(s_araddr), .arvalid(s_arvalid), .arready(s_arready),
.rdata(s_rdata), .rresp(s_rresp), .rvalid(s_rvalid), .rready(s_rready),
.o_wr_en(axi_wr_en), .o_wr_addr(axi_wr_addr), .o_wr_data(axi_wr_data),
.o_rd_en(axi_rd_en), .o_rd_addr(axi_rd_addr), .i_rd_data(rf_rd)
);
// Register file
regfile u_rf (
.clk(clk), .rst_n(rst_n),
.wr(rf_wr), .addr(rf_addr), .wd(rf_wd), .rd(rf_rd),
.dma_src(dma_src), .dma_dst(dma_dst), .dma_len(dma_len),
.fir_en(fir_en), .dma_start(dma_start),
.ca(ca), .cd(cd), .cwe(cwe), .irq_en(irq_en), .irq_out(irq_out),
.dma_done(dma_done), .dma_busy(dma_busy), .fir_done(fir_done)
);
// DMA controller
dma_ctrl u_dma (
.clk(clk), .rst_n(rst_n),
.start(dma_start), .busy(dma_busy), .done(dma_done),
.cfg_src(dma_src), .cfg_dst(dma_dst), .cfg_len(dma_len),
.mem_re(dma_mre), .mem_ra(dma_mra), .mem_rd(dma_mrd), .mem_rv(dma_mrv),
.mem_we(dma_mwe), .mem_wa(dma_mwa), .mem_wd(dma_mwd),
.fir_vld(fir_vld), .fir_dat(fir_dat), .fir_rdy(fir_en)
);
// FIR filter
fir_filter_8tap u_fir (
.clk(clk), .rst_n(rst_n),
.en(fir_en), .dv_in(fir_vld), .din(fir_dat),
.ca(ca), .cd(cd), .cwe(cwe),
.dv_out(fir_done), .dout()
);
assign irq = irq_out;
assign test_rd = rf_rd;
endmodule
// ============================================================================
// 6. MAKERCHIP TOP
// ============================================================================
module top (
input logic clk,
input logic reset_async,
output logic passed,
output logic failed
);
// Reset sync
logic rst_n;
logic [3:0] rchain;
always @(posedge clk)
if (reset_async) begin
rchain <= 4'b0000;
rst_n <= 1'b0;
end else begin
rchain <= {rchain[2:0], 1'b1};
rst_n <= rchain[3];
end
// Cycle counter
logic [9:0] cyc;
always @(posedge clk)
if (reset_async || !rst_n)
cyc <= 0;
else
cyc <= cyc + 1;
// Test signals
logic t_wr;
logic [7:0] t_addr;
logic [31:0] t_wd;
logic [31:0] t_rd;
// AXI signals (tied off for now)
logic [31:0] ax_awaddr, ax_araddr, ax_wdata;
logic ax_awvalid, ax_wvalid, ax_arvalid, ax_bready, ax_rready;
logic [31:0] ax_rdata;
logic [1:0] ax_bresp, ax_rresp;
logic ax_awready, ax_wready, ax_bvalid, ax_arready, ax_rvalid;
logic dut_irq;
// Testbench: t_wr stays high from cycle 6 onwards (proven approach)
always @(posedge clk)
if (reset_async || !rst_n) begin
t_wr <= 0;
t_addr <= 0;
t_wd <= 0;
passed <= 0;
failed <= 0;
end else begin
if (cyc >= 10'd6) begin
t_wr <= 1;
if (cyc == 10'd6) begin
t_addr <= 8'h10; // DMA_SRC
t_wd <= 32'hDEAD_BEEF;
end
end
// Check at cycle 10
if (cyc == 10'd10) begin
if (t_rd == 32'hDEAD_BEEF)
passed <= 1;
else
failed <= 1;
end
end
// Tie off AXI (not used in this test)
assign ax_awaddr = 0;
assign ax_araddr = 0;
assign ax_wdata = 0;
assign ax_awvalid = 0;
assign ax_wvalid = 0;
assign ax_arvalid = 0;
assign ax_bready = 0;
assign ax_rready = 0;
// DUT
dsp_soc u_dut (
.clk(clk),
.rst_n(rst_n),
// AXI-Lite port
.s_awaddr(ax_awaddr), .s_awvalid(ax_awvalid), .s_awready(ax_awready),
.s_wdata(ax_wdata), .s_wstrb(4'hF), .s_wvalid(ax_wvalid), .s_wready(ax_wready),
.s_bresp(ax_bresp), .s_bvalid(ax_bvalid), .s_bready(ax_bready),
.s_araddr(ax_araddr), .s_arvalid(ax_arvalid), .s_arready(ax_arready),
.s_rdata(ax_rdata), .s_rresp(ax_rresp), .s_rvalid(ax_rvalid), .s_rready(ax_rready),
// Test port
.test_wr(t_wr),
.test_addr(t_addr),
.test_wd(t_wd),
.test_rd(t_rd),
.irq(dut_irq)
);
endmodule
2.3 模拟仿真
1 仿真测试

3 CPUIator NiosII交互指示SoC
3.1 系统及其创建
1 应用系统创建
- 浏览器访问CPUlator网站
- 选择Nios II处理器架构
- 加载预设配置(或使用自定义.gpj项目文件)

2 Nios II SoC 系统架构概览

3 外设地址映射表(CPUlator 预设配置)
|--------------------------|------------|--------|--------|------------------------|
| 外设模块 | 基地址 | 宽度 | 方向 | 功能说明 |
| jtag_uart_0 | 0x10001000 | --- | --- | JTAG 调试串口 |
| onchip_memory | 0x00000000 | 128KB | --- | 片上 RAM |
| sys_clk_timer | 0x10002000 | --- | --- | 系统时钟定时器 |
| pio_keys (按键) | 0x10000060 | 4 bit | 输入 | 4 个按键 KEY3:0 |
| pio_switches (开关) | 0x10000050 | 10 bit | 输入 | 10 位拨码开关 SW9:0 |
| pio_leds (LED) | 0x10000040 | 10 bit | 输出 | 10 个 LED 灯 LEDR9:0 |
| pio_hex_low (低两位数码管) | 0x10000020 | 32 bit | 输出 | HEX3~HEX0 |
| | | | | |
4 PIO 寄存器偏移
每个PIO模块包含以下寄存器(偏移相对于基地址):
|--------|----------------|--------------|------------------------|
| 偏移 | 寄存器 | 读/ 写 | 说明 |
| 0x00 | Data | R/W | 数据寄存器:输入=读引脚状态,输出=写输出值 |
| 0x04 | Direction | R/W | 方向寄存器:0=输入,1=输出 |
| 0x08 | Interrupt Mask | R/W | 中断屏蔽寄存器 |
| 0x0C | Edge Capture | R/W | 边沿捕获寄存器 |
3.2 功能编码实现
完整的C语言编码,包括:头文件与宏定义、七段数码管编码表、外设驱动函数、主程序实现多模式交互。
cpp
/*==========================================================================
* 文件: nios2_soc_io.c
* 平台: CPUlator 在线仿真 --- Nios II (nios-de1soc 预设)
* 功能: 按键/开关输入 + LED/七段数码管输出,4种运行模式
* 模式0: SW[9:0] → LEDR[9:0] 镜像 + HEX2~0 显示开关值
* 模式1: KEY0=+1, KEY1=-1 计数器,LED+HEX显示
* 模式2: LED流水灯,KEY2=暂停/继续
* 模式3: HEX5~0 十六进制自动递增计数器
* 切换: KEY3 下降沿切换模式
*========================================================================*/
/* ========================== 基础类型 ========================== */
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
/* ========================== PIO 寄存器偏移 ========================== */
#define PIO_DATA_OFFSET 0x00
#define PIO_DIRECTION_OFFSET 0x04
#define PIO_IRQ_MASK_OFFSET 0x08
#define PIO_EDGE_CAP_OFFSET 0x0C
/* ========================== 外设基地址 (nios-de1soc) ========================== */
#define PIO_LEDS_BASE 0xff200000 /* LEDR[9:0] 输出 */
#define PIO_HEX_LOW_BASE 0xff200020 /* HEX3~HEX0 七段数码管 */
#define PIO_HEX_HIGH_BASE 0xff200030 /* HEX5~HEX4 七段数码管 */
#define PIO_SWITCHES_BASE 0xff200040 /* SW[9:0] 输入 */
#define PIO_KEYS_BASE 0xff200050 /* KEY[3:0] 输入 */
#define JTAG_UART_BASE 0xff201000 /* JTAG UART 调试串口 */
#define SYS_TIMER_BASE 0xff202000 /* 系统定时器 */
/* ========================== JTAG UART 寄存器偏移 ========================== */
#define JTAG_UART_DATA 0x00
#define JTAG_UART_CONTROL 0x04
/* ======================================================================
* I/O 访问 --- 内联汇编精确控制指令类型 *
* ldwio/stwio → 仅用于外设地址 (0xFF20xxxx, 绕过 cache)
* ldw/stw → 编译器对普通 C 变量自动生成 (经过 cache) *
/* ======================================================================*/
static inline uint32_t IORD(uint32_t base, uint32_t reg) {
uint32_t val;
__asm__ volatile (
"ldwio %0, 0(%1)"
: "=r"(val)
: "r"(base + reg)
);
return val;
}
static inline void IOWR(uint32_t base, uint32_t reg, uint32_t val) {
__asm__ volatile (
"stwio %0, 0(%1)"
:
: "r"(val), "r"(base + reg)
: "memory"
);
}
/* ======================================================================
* 七段数码管段码表 (共阳极, 低电平点亮)-- 0 = 段亮, 1 = 段灭
* aaa bit0 = a段 bit4 = e段
* f b bit1 = b段 bit5 = f段
* ggg bit2 = c段 bit6 = g段
* e c bit3 = d段 bit7 = dp
* ddd dp
* ======================================================================*/
static const uint8_t SEG7_TABLE[16] = {
/* 0 */ 0xC0, /* 1100 0000 a,b,c,d,e,f 亮 */
/* 1 */ 0xF9, /* 1111 1001 b,c 亮 */
/* 2 */ 0xA4, /* 1010 0100 a,b,d,e,g 亮 */
/* 3 */ 0xB0, /* 1011 0000 a,b,c,d,g 亮 */
/* 4 */ 0x99, /* 1001 1001 b,c,f,g 亮 */
/* 5 */ 0x92, /* 1001 0010 a,c,d,f,g 亮 */
/* 6 */ 0x82, /* 1000 0010 a,c,d,e,f,g 亮 */
/* 7 */ 0xF8, /* 1111 1000 a,b,c 亮 */
/* 8 */ 0x80, /* 1000 0000 全部亮 */
/* 9 */ 0x90, /* 1001 0000 a,b,c,d,f,g 亮 */
/* A */ 0x88, /* 1000 1000 a,b,c,e,f,g 亮 */
/* b */ 0x83, /* 1000 0011 c,d,e,f,g 亮 */
/* C */ 0xC6, /* 1100 0110 a,d,e,f 亮 */
/* d */ 0xA1, /* 1010 0001 b,c,d,e,g 亮 */
/* E */ 0x86, /* 1000 0110 a,d,e,f,g 亮 */
/* F */ 0x8E /* 1000 1110 a,e,f,g 亮 */
};
/* ========================== 全局变量 (无 volatile) ==================== */
typedef enum {
MODE_SWITCH_MIRROR = 0, /* 开关→LED 镜像 */
MODE_KEY_COUNTER, /* 按键计数器 */
MODE_LED_SCAN, /* 流水灯 */
MODE_HEX_COUNTER, /* HEX 计数器 */
MODE_COUNT
} run_mode_t;
static run_mode_t g_mode = MODE_SWITCH_MIRROR;
static uint32_t g_key_count = 0;
static uint32_t g_led_pat = 0x001;
static uint32_t g_hex_count = 0;
static uint8_t g_scan_pause = 0;
/* ==========================工具函数=====================================*/
static void delay(uint32_t count) { // 延时
while (count--) {
__asm__ volatile ("nop");
}
}
static uint32_t read_keys(void) { // 按键读取 (低电平有效: 按下=0)
return IORD(PIO_KEYS_BASE, PIO_DATA_OFFSET) & 0x0F;
}
static uint32_t read_switches(void) { //开关读取
return IORD(PIO_SWITCHES_BASE, PIO_DATA_OFFSET) & 0x3FF;
}
static void write_leds(uint32_t val) { //LED 写入
IOWR(PIO_LEDS_BASE, PIO_DATA_OFFSET, val & 0x3FF);
}
static uint32_t led_shift_left(uint32_t cur) { //LED 流水: 单灯左移
uint32_t next = (cur << 1) & 0x3FF;
if (next == 0) next = 0x001;
return next;
}
/* ======================七段数码管驱动===================================
/* 在指定数码管位置显示一个十六进制数字
* pos: 0~5 (0=HEX0最右, 5=HEX5最左)
* value: 0~15
* 布局:
* PIO_HEX_LOW [31:0] = HEX3(31:24) | HEX2(23:16) | HEX1(15:8) | HEX0(7:0)
* PIO_HEX_HIGH [31:0] = (高位未用) | HEX5(15:8) | HEX4(7:0)
*/
static void hex_display_digit(uint8_t pos, uint8_t value) {
uint32_t seg = SEG7_TABLE[value & 0x0F];
uint32_t shift, mask, cur;
if (pos < 4) {
/* HEX0~3 → PIO_HEX_LOW */
shift = pos * 8;
mask = ~(0xFFu << shift);
cur = IORD(PIO_HEX_LOW_BASE, PIO_DATA_OFFSET);
cur = (cur & mask) | (seg << shift);
IOWR(PIO_HEX_LOW_BASE, PIO_DATA_OFFSET, cur);
} else {
/* HEX4~5 → PIO_HEX_HIGH */
shift = (pos - 4) * 8;
mask = ~(0xFFu << shift);
cur = IORD(PIO_HEX_HIGH_BASE, PIO_DATA_OFFSET);
cur = (cur & mask) | (seg << shift);
IOWR(PIO_HEX_HIGH_BASE, PIO_DATA_OFFSET, cur);
}
}
/* 6位数码管显示24位十六进制数 */
static void hex_display_value(uint32_t value) {
int i;
for (i = 0; i < 6; i++) {
hex_display_digit((uint8_t)i, (uint8_t)((value >> (i * 4)) & 0x0F));
}
}
/* 清除全部数码管 (消隐: 全1) */
static void hex_display_clear(void) {
IOWR(PIO_HEX_LOW_BASE, PIO_DATA_OFFSET, 0xFFFFFFFF);
IOWR(PIO_HEX_HIGH_BASE, PIO_DATA_OFFSET, 0xFFFFFFFF);
}
/* ======================JTAG UART 调试输出============================*/
static void jtag_putchar(char c) {
/* 等待发送 FIFO 有空位: control[31:16] = WSPACE > 0 */
while ((IORD(JTAG_UART_BASE, JTAG_UART_CONTROL) & 0xFFFF0000) == 0) ;
IOWR(JTAG_UART_BASE, JTAG_UART_DATA, (uint32_t)(uint8_t)c);
}
static void jtag_puts(const char *s) {
while (*s) {
if (*s == '\n') jtag_putchar('\r');
jtag_putchar(*s++);
}
}
static void jtag_put_hex(uint32_t value) {
static const char hexch[] = "0123456789ABCDEF";
int i; int started = 0;
jtag_putchar('0');
jtag_putchar('x');
for (i = 28; i >= 0; i -= 4) {
uint8_t nibble = (uint8_t)((value >> i) & 0x0F);
if (nibble || started || i == 0) {
jtag_putchar(hexch[nibble]);
started = 1;
}
}
}
/* ========================各模式处理函数=================================*/
/* HEX5 始终显示当前模式编号 */
static void show_mode_indicator(void) {
hex_display_digit(5, (uint8_t)g_mode);
}
/* ---- 模式0: 开关→LED 镜像 ---- */
static void mode_switch_mirror(void) {
uint32_t sw = read_switches();
write_leds(sw);
/* HEX0=低4位, HEX1=中间4位, HEX2=SW9~8 */
hex_display_digit(0, (uint8_t)(sw & 0x0F));
hex_display_digit(1, (uint8_t)((sw >> 4) & 0x0F));
hex_display_digit(2, (uint8_t)((sw >> 8) & 0x03));
}
/* ---- 模式1: 按键计数 ---- */
static void mode_key_counter(void) {
uint32_t keys = read_keys();
if ((keys & 0x01) == 0) { g_key_count++; delay(200000); } /* KEY0 +1 */
if ((keys & 0x02) == 0) { g_key_count--; delay(200000); } /* KEY1 -1 */
write_leds(g_key_count & 0x3FF);
hex_display_digit(0, (uint8_t)(g_key_count & 0x0F));
hex_display_digit(1, (uint8_t)((g_key_count >> 4) & 0x0F));
jtag_puts("CNT=");
jtag_put_hex(g_key_count);
jtag_puts("\n");
}
/* ---- 模式2: 流水灯 ---- */
static void mode_led_scan(void) {
/* KEY2 按下 → 暂停/继续 */
if ((read_keys() & 0x04) == 0) {
g_scan_pause = !g_scan_pause;
delay(200000);
}
if (!g_scan_pause) {
write_leds(g_led_pat);
g_led_pat = led_shift_left(g_led_pat);
hex_display_digit(0, (uint8_t)(g_led_pat & 0x0F));
}
delay(100000);
}
/* ---- 模式3: 十六进制计数器 ---- */
static void mode_hex_counter(void) {
hex_display_value(g_hex_count);
write_leds(g_hex_count & 0x3FF);
g_hex_count++;
if (g_hex_count > 0xFFFFFF) g_hex_count = 0;
delay(150000);
}
/* ==========================系统初始化=================================*/
static void system_init(void) {
/* 清除输出 */
write_leds(0x000);
hex_display_clear();
/* PIO 方向: 输入=0x0, 输出=全1 */
IOWR(PIO_KEYS_BASE, PIO_DIRECTION_OFFSET, 0x00000000);
IOWR(PIO_SWITCHES_BASE, PIO_DIRECTION_OFFSET, 0x00000000);
IOWR(PIO_LEDS_BASE, PIO_DIRECTION_OFFSET, 0x000003FF);
IOWR(PIO_HEX_LOW_BASE, PIO_DIRECTION_OFFSET, 0xFFFFFFFF);
IOWR(PIO_HEX_HIGH_BASE, PIO_DIRECTION_OFFSET, 0xFFFFFFFF);
/* 清除按键边沿捕获 + 禁止中断 */
IOWR(PIO_KEYS_BASE, PIO_EDGE_CAP_OFFSET, 0x0F);
IOWR(PIO_KEYS_BASE, PIO_IRQ_MASK_OFFSET, 0x00);
/* 启动信息 */
jtag_puts("\n========================================\n");
jtag_puts(" Nios II SoC IO Demo (nios-de1soc)\n");
jtag_puts("========================================\n");
jtag_puts(" Mode 0: SW -> LED Mirror\n");
jtag_puts(" Mode 1: Key Counter\n");
jtag_puts(" Mode 2: LED Scan (KEY2=Pause)\n");
jtag_puts(" Mode 3: Hex Counter\n");
jtag_puts(" KEY3: Switch Mode\n");
jtag_puts("----------------------------------------\n");
}
/* =============================主函数===================================*/
int main(void) {
uint32_t prev_key3 = 1; /* KEY3 上次状态, 未按下=1 */
system_init();
jtag_puts("[MAIN] Entering loop.\n");
while (1) {
/* ---- KEY3 下降沿检测 (1→0 = 按下) ---- */
uint32_t key3_now = read_keys() & 0x08;
if (prev_key3 == 1 && key3_now == 0) {
g_mode = (run_mode_t)((g_mode + 1) % MODE_COUNT);
jtag_puts("MODE=");
jtag_put_hex((uint32_t)g_mode);
jtag_puts("\n");
delay(200000);
}
prev_key3 = key3_now;
/* ---- 模式指示 ---- */
show_mode_indicator();
/* ---- 执行当前模式 ---- */
switch (g_mode) {
case MODE_SWITCH_MIRROR: mode_switch_mirror(); break;
case MODE_KEY_COUNTER: mode_key_counter(); break;
case MODE_LED_SCAN: mode_led_scan(); break;
case MODE_HEX_COUNTER: mode_hex_counter(); break;
default: g_mode = MODE_SWITCH_MIRROR; break;
}
}
return 0;
}
3.3 仿真测试跟踪
1 编译运行
编码、确认外设地址、编译与下载、运行,进入各种模式跟踪测试。

2 跟踪测试
++A++ ++.测试模式0:++ ++开关镜像++

跟踪寄存器变化:
r4 ← IORD(0x10000050, 0x00) ; 读到 SW 值
r5 ← r4 & 0x3FF ; 掩码
IOWR(0x10000040, 0x00, r5) ; 写入 LED
++B++ ++.测试模式1:++ ++按键计数(KEY3++ ++切换到此模式)++

调试技巧:
- 使用 "Step" 单步执行到 delay() 前
- 在 Registers 面板观察 r4(按键值) 的变化
- 在 Memory 面板观察 g_key_count 变量地址处的值
++C++ ++.测试模式2:++ ++流水灯(KEY3++ ++再次切换)++

++D++ ++.测试模式3:++ ++十六进制计数器(KEY3++ ++再次切换)++

++E++ ++.寄存器级调试跟踪++
在 CPUlator 的 Registers 和 Memory 面板中,可以进行指令级跟踪。
++F++ ++.JTAG UART++ ++调试跟踪++
CPUlator 的 JTAG UART 窗口实时显示串口输出