RISC-V项目(2)---增加B型指令和UART

1、增加B型指令的译码器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
`timescale 1ns / 1ps                                                                                     
/****************************************Copyright (c)**************************************************
**----------------------------------------File Info-----------------------------------------------------
** File name : decode
** Last modified Date : 2025-11-01
** Last Version : 1.0
** Descriptions : decode
**------------------------------------------------------------------------------------------------------
** Created by : FPGACoreCG
** Created date : 2025-11-01
** Version : 1.0
** Descriptions : The original version
**------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
** Version:
** Descriptions:
**
**------------------------------------------------------------------------------------------------------
*******************************************************************************************************/

`include "define.sv"

module decode #(
parameter AW = 32 ,
parameter DW = 32
)(
input logic [AW-1:0] instr_addr_in ,
input logic [DW-1:0] instr_in ,
//to register
output logic [4:0] rd_rs1_addr ,
output logic [4:0] rd_rs2_addr ,
//from register
input logic [DW-1:0] rd_rs1_data ,
input logic [DW-1:0] rd_rs2_data ,
//to instr execute
output logic [DW-1:0] op1_out ,
output logic [DW-1:0] op2_out
);

logic [6:0] opcode ;
logic [4:0] rd ;
logic [2:0] func3 ;
logic [4:0] rs1 ;
logic [4:0] rs2 ;
logic [6:0] func7 ;
logic [31:0] imm ;


assign opcode = instr_in[6:0] ;
assign rd = instr_in[11:7] ;
assign func3 = instr_in[14:12] ;
assign rs1 = instr_in[19:15] ;
assign rs2 = instr_in[24:20] ;
assign func7 = instr_in[31:25] ;

always_comb begin
case(opcode)
`INST_TYPE_I,`INST_TYPE_L,`INST_JALR:
imm = {{20{instr_in[31]}},instr_in[31:20]};
`INST_TYPE_S:
imm = {{20{instr_in[31]}},instr_in[31:25],instr_in[11:7]};
`INST_TYPE_B:
imm = {{20{instr_in[31]}},instr_in[7],instr_in[30:25],instr_in[11:8],1'b0};
`INST_JAL:
imm = {{12{instr_in[31]}},instr_in[19:12],instr_in[20],instr_in[30:21], 1'b0};
`INST_LUI,`INST_LUIPC:
imm = {instr_in[31:12],12'h0};
default:imm = 32'h0;
endcase
end

always_comb begin
case(opcode)
`INST_TYPE_I:begin
case(func3)
`INST_ADDI:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = 5'h0 ;
op1_out = rd_rs1_data ;
op2_out = imm ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end
`INST_TYPE_R_M:begin
case(func3)
`INST_ADD_SUB:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = rs2 ;
op1_out = rd_rs1_data ;
op2_out = rd_rs2_data ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end
`INST_TYPE_B:begin
case(func3)
`INST_BEQ,`INST_BNE,`INST_BLT,`INST_BGE,`INST_BLTU,`INST_BGEU:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = rs2 ;
op1_out = rd_rs1_data ;
op2_out = rd_rs2_data ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end
`INST_JAL:begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = 32'h0 ;
op2_out = imm ;
end
`INST_JALR:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = 5'h0 ;
op1_out = rd_rs1_data ;
op2_out = imm ;
end
`INST_LUI:begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = 32'h0 ;
op2_out = imm ;
end
`INST_LUIPC:begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = instr_addr_in ;
op2_out = imm ;
end
`INST_NOP_OP: begin
rd_rs1_addr = 5'h0 ;
rd_rs2_addr = 5'h0 ;
op1_out = 32'h0 ;
op2_out = 32'h0 ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end

endmodule

其中最主要的是下面这段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
`INST_TYPE_B:begin
case(func3)
`INST_BEQ,`INST_BNE,`INST_BLT,`INST_BGE,`INST_BLTU,`INST_BGEU:begin
rd_rs1_addr = rs1 ;
rd_rs2_addr = rs2 ;
op1_out = rd_rs1_data ;
op2_out = rd_rs2_data ;
end
default:begin
rd_rs1_addr = 'h0 ;
rd_rs2_addr = 'h0 ;
op1_out = 'h0 ;
op2_out = 'h0 ;
end
endcase
end

输入:指令中的 rs1(源寄存器1索引)和 rs2(源寄存器2索引)。

动作:去寄存器堆(RegFile)里把这两个寄存器的值读出来。

输出:把读出来的值赋给 op1_out 和 op2_out,送给下一级(ALU)。

2、执行单元代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
`timescale 1ns / 1ps
/****************************************Copyright (c)**************************************************
**----------------------------------------File Info-----------------------------------------------------
** File name : execute.v
** Last modified Date : 2025-11-01
** Last Version : 1.0
** Descriptions : RISC-V 五级流水线 —— 执行阶段 (Execute Stage)
** 负责 ALU 运算、分支判断 (Branch) 和跳转地址计算 (Jump)
**------------------------------------------------------------------------------------------------------
********************************************************************************************************/

// 包含宏定义文件,里面定义了操作码 (e.g., `INST_TYPE_I) 和功能码 (e.g., `INST_ADDI)
`include "define.sv"

module execute #(
parameter AW = 32, // Address Width: 地址位宽 (PC宽度)
parameter DW = 32 // Data Width: 数据位宽 (寄存器宽度)
)(
//-------------------------------------------------------------------------
// 输入信号:来自 Decode (译码) 阶段
//-------------------------------------------------------------------------
input logic [AW-1:0] instr_addr, // 当前指令的 PC 地址 (用于计算相对跳转的目标地址)
input logic [DW-1:0] instr, // 当前指令的原始机器码 (用于提取 funct3/funct7)
input logic [DW-1:0] op1, // 操作数 1 (通常来自 rs1)
input logic [DW-1:0] op2, // 操作数 2 (来自 rs2 或 扩展后的立即数 Imm)

//-------------------------------------------------------------------------
// 输出信号:发送给 Control / IF 阶段 (用于控制跳转)
//-------------------------------------------------------------------------
output logic wr_reg_en, // 写寄存器使能信号 (1: 写, 0: 不写)
output logic [4:0] wr_reg_addr, // 写回的目标寄存器索引 (rd)
output logic [DW-1:0] wr_reg_data, // 写回的数据 (ALU 计算结果)

//-------------------------------------------------------------------------
// 输出信号:发送给 Control / IF 阶段 (用于控制跳转)
//-------------------------------------------------------------------------
output logic jump_en, // 跳转使能 (High有效,表示需要跳转)
output logic [AW-1:0] jump_addr, // 跳转的目标地址 (Target PC)
output logic jump_hold // 流水线暂停请求 (预留信号,目前逻辑中全为 0)
);

//=========================================================================
// 1. 内部信号定义 & 指令字段切片
//=========================================================================
logic [6:0] opcode; // 操作码 (低7位)
logic [4:0] rd; // 目标寄存器索引 (instr[11:7])
logic [2:0] func3; // 功能码3位 (instr[14:12])
logic [6:0] func7; // 功能码7位 (instr[31:25])
logic [31:0] imm; // 本地解出的立即数 (主要用于 B-Type 分支指令)
logic equal; // 比较标志:相等
logic less_signed; // 比较标志:有符号小于
logic less_unsigned; // 比较标志:无符号小于
logic [AW-1:0] jump_imm; // B-Type 指令计算出的跳转目标地址

//=========================================================================
// 2. 组合逻辑连线 (Assign)
//=========================================================================

// --- 指令解码 (虽然 Decode 阶段做过,但 EX 阶段也需要这些具体位来做判断) ---
assign opcode = instr[6:0];
assign rd = instr[11:7];
assign func3 = instr[14:12];
assign func7 = instr[31:25];

// --- 立即数生成 (专门针对 B-Type 指令) ---
// RISC-V 的 B-Type 立即数是打乱拼接的:imm[12|10:5|4:1|11]
// 这里的拼接逻辑:{{20{符号位}}, bit12, bit10:5, bit4:1, 0}
// 注意:最低位补0,因为跳转地址必须是偶数 (2字节对齐)
assign imm = {{20{instr[31]}}, instr[7], instr[30:25], instr[11:8], 1'b0};

// --- 硬件比较器 (ALU Comparator) ---
// 预先并行计算所有比较结果,供分支指令选择使用
assign equal = (op1 == op2) ? 1'b1 : 1'b0; // 判断相等 (BEQ/BNE)
assign less_signed = ($signed(op1) < $signed(op2)) ? 1'b1 : 1'b0;// 有符号小于 (BLT/BGE)
assign less_unsigned = (op1 < op2) ? 1'b1 : 1'b0; // 无符号小于 (BLTU/BGEU)

// --- 计算分支目标地址 ---
// Target = Current PC + Offset (B-Type 的 offset 就是上面切出来的 imm)
assign jump_imm = instr_addr + imm;

//=========================================================================
// 3. 核心执行逻辑 (ALU & Branch Control)
//=========================================================================
always_comb begin
case(opcode)

//-----------------------------------------------------------------
// I-Type 指令: 立即数运算 (e.g., ADDI x1, x2, 10)
//-----------------------------------------------------------------
`INST_TYPE_I: begin
case(func3)
`INST_ADDI: begin
wr_reg_en = 1'b1; // ADDI 需要写回结果
wr_reg_addr = rd; // 写到 rd
wr_reg_data = op1 + op2; // ALU 动作:加法 (op2 此时是立即数)
jump_en = 1'b0; // 不涉及跳转
jump_addr = 'h0;
jump_hold = 1'b0;
end
// 如有其他 I 型指令 (SLTI, ANDI 等),需在此添加 case
default: begin
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0;
jump_en = 1'b0; jump_addr = 'h0; jump_hold = 1'b0;
end
endcase
end

//-----------------------------------------------------------------
// R-Type / M-Type 指令: 寄存器-寄存器运算 (e.g., ADD, SUB)
//-----------------------------------------------------------------
`INST_TYPE_R_M: begin
case(func3)
`INST_ADD_SUB: begin
// 通过 func7 的第 5 位 (bit 30 in instr) 区分 ADD 和 SUB
// 0000000 -> ADD; 0100000 -> SUB
if(func7 == 7'b000_0000) begin // ADD
wr_reg_en = 1'b1;
wr_reg_addr = rd;
wr_reg_data = op1 + op2; // 加法
jump_en = 1'b0; jump_addr = 'h0; jump_hold = 1'b0;
end
else begin // SUB
wr_reg_en = 1'b1;
wr_reg_addr = rd;
wr_reg_data = op1 - op2; // 减法
jump_en = 1'b0; jump_addr = 'h0; jump_hold = 1'b0;
end
end
// 如有乘除法 (M扩展),需在此添加 case
default: begin
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0;
jump_en = 1'b0; jump_addr = 'h0; jump_hold = 1'b0;
end
endcase
end

//-----------------------------------------------------------------
// B-Type 指令: 条件分支 (e.g., BEQ, BNE)
// 只要判断条件成立,就拉高 jump_en,并给出跳转地址
//-----------------------------------------------------------------
`INST_TYPE_B: begin
case(func3)
`INST_BEQ: begin // Branch if Equal
wr_reg_en = 1'b0; // 分支指令不写寄存器
wr_reg_addr = 5'h0;
wr_reg_data = 'h0;
jump_en = equal; // 如果相等,则跳转
jump_addr = equal ? jump_imm : 'h0;
jump_hold = 1'b0;
end
`INST_BNE: begin // Branch if Not Equal
wr_reg_en = 1'b0;
wr_reg_addr = 5'h0;
wr_reg_data = 'h0;
jump_en = ~equal; // 如果不相等,则跳转
jump_addr = ~equal ? jump_imm : 'h0;
jump_hold = 1'b0;
end
`INST_BLT: begin // Branch if Less Than (Signed)
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0; jump_hold = 1'b0;
jump_en = less_signed;
jump_addr = less_signed ? jump_imm : 'h0;
end
`INST_BGE: begin // Branch if Greater/Equal (Signed)
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0; jump_hold = 1'b0;
jump_en = ~less_signed; // not less than = greater or equal
jump_addr = ~less_signed ? jump_imm : 'h0;
end
`INST_BLTU: begin // Branch if Less Than (Unsigned)
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0; jump_hold = 1'b0;
jump_en = less_unsigned;
jump_addr = less_unsigned ? jump_imm : 'h0;
end
`INST_BGEU: begin // Branch if Greater/Equal (Unsigned)
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0; jump_hold = 1'b0;
jump_en = ~less_unsigned;
jump_addr = ~less_unsigned ? jump_imm : 'h0;
end
default: begin
wr_reg_en = 1'b0; wr_reg_addr = 5'h0; wr_reg_data = 'h0;
jump_en = 1'b0; jump_addr = 'h0; jump_hold = 1'b0;
end
endcase
end

//-----------------------------------------------------------------
// J-Type 指令: JAL (Jump and Link)
// 功能:1. 跳转; 2. 将下一条指令地址 (PC+4) 保存到 rd (通常是 ra)
//-----------------------------------------------------------------
`INST_JAL: begin
wr_reg_en = 1'b1; // 需要写寄存器 (Link操作)
wr_reg_addr = rd;
wr_reg_data = instr_addr + 32'h4; // 保存 PC+4 到 rd
jump_en = 1'b1; // 必须跳转
// JAL 的跳转偏移量较大,通常在 Decode 阶段解出放在 op2,或者此处重新计算
// 代码中使用了 `instr_addr + op2`,暗示 op2 传递了 J-Type 的 offset
jump_addr = instr_addr + op2;
jump_hold = 1'b0;
end

//-----------------------------------------------------------------
// I-Type 跳转: JALR (Jump and Link Register)
// 功能:跳到 (rs1 + offset) 的地址
//-----------------------------------------------------------------
`INST_JALR: begin
wr_reg_en = 1'b1;
wr_reg_addr = rd;
wr_reg_data = instr_addr + 32'h4; // 保存 PC+4 到 rd
jump_en = 1'b1;
// JALR 的目标地址是寄存器值 (op1) + 偏移量 (op2)
jump_addr = op1 + op2;
jump_hold = 1'b0;
end

//-----------------------------------------------------------------
// U-Type 指令: LUI (Load Upper Immediate)
//-----------------------------------------------------------------
`INST_LUI: begin
wr_reg_en = 1'b1;
wr_reg_addr = rd;
wr_reg_data = op2; // op2 已经是 Decode 阶段处理好的 {imm, 12'b0}
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold = 1'b0;
end

//-----------------------------------------------------------------
// U-Type 指令: LUIPC (Load Upper Immediate to PC)
// 结果 = PC + {imm, 12'b0}
//-----------------------------------------------------------------
`INST_LUIPC: begin
wr_reg_en = 1'b1;
wr_reg_addr = rd;
wr_reg_data = op1 + op2; // op1 是 PC,op2 是移位后的立即数
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold = 1'b0;
end

//-----------------------------------------------------------------
// 内部空指令 / 气泡 (NOP)
//-----------------------------------------------------------------
`INST_NOP_OP: begin
wr_reg_en = 1'b0;
wr_reg_addr = 5'h0;
wr_reg_data = 'h0;
jump_en = 1'b0;
jump_addr = 'h0;
jump_hold = 1'b0;
end

//-----------------------------------------------------------------
// 默认情况 (非法指令或未实现指令)
//-----------------------------------------------------------------
default: begin
wr_reg_en = 1'b0;
wr_reg_addr = 5'h0;
wr_reg_data = 'h0;
jump_en = 1'b0; // 注意:原代码此处为 1'b1 可能是为了容错或复位,通常应为 0
jump_addr = 'h0;
jump_hold = 1'b0;
end
endcase
end

endmodule

3、测试文件

这段代码使用了 Xilinx Vivado 专用的 综合属性 (* mark_debug = "true" *)**,将其标记在执行阶段(Execute Stage)的关键信号(如指令地址 instr_addr、操作数 op1/op2、写回数据 wr_reg_data 等)之前。这相当于给这些信号颁发了“免死金牌”“自动挂号证”:它的核心作用是强制综合器保留这些信号(防止被优化删除)**,并使它们在 Vivado 的 Set Up Debug 步骤中能被自动识别,从而方便地添加到 ILA(集成逻辑分析仪) 中。其最终目的是为了在 FPGA 上板调试 时,能够像做“心电图”一样实时抓取并观察 CPU 运算核心的内部状态,快速定位指令执行或逻辑运算的 Bug。

1
2
3
4
5
6
7
(*mark_debug = "true"*)logic	[AW-1:0]								execute_instr_addr		; 
(*mark_debug = "true"*)logic [DW-1:0] execute_instr ;
(*mark_debug = "true"*)logic [DW-1:0] execute_op1 ;
(*mark_debug = "true"*)logic [DW-1:0] execute_op2 ;
(*mark_debug = "true"*)logic wr_reg_en ;
(*mark_debug = "true"*)logic [4:0] wr_reg_addr ;
(*mark_debug = "true"*)logic [DW-1:0] wr_reg_data ;

4、LED

  • LED闪烁代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
`timescale 1ns / 1ps

//`define SIM 这里注释掉SIM的定义,表明不进入测试模式

module blink_led(
input logic clk ,
input logic rst_n ,
output logic led
);
`ifdef SIM
localparam CLK_FRQ = 10;
`else
localparam CLK_FRQ = 100000000;
`endif

logic [31:0] cnt ;

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 'h0;
else if(cnt == CLK_FRQ - 1)
cnt <= 'h0;
else
cnt <= cnt + 'h1;

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
led <= 1'b0;
else if(cnt == CLK_FRQ - 1)
led <= ~led;

endmodule
  • LED流水灯
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module flow_led(
input logic clk ,
input logic rst_n ,
output logic [3:0] led
);
`ifdef SIM
localparam CLK_FRQ = 10;
`else
localparam CLK_FRQ = 100000000;
`endif

logic [31:0] cnt ;

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 'h0;
else if(cnt == CLK_FRQ - 1)
cnt <= 'h0;
else
cnt <= cnt + 'h1;

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
led <= 4'b0001;
else if(cnt == CLK_FRQ - 1)
led <= {led[0],led[3:1]};

endmodule

呼吸灯:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//`define SIM
module breath_led(
input logic clk ,
input logic rst_n ,
output logic led
);

`ifdef SIM
localparam TOTAL_PWM_NUM = 8 ;
localparam SINGLE_PWM_NUM = 8 ;
localparam CELL_NUM = 2 ;
`else
localparam TOTAL_PWM_NUM = 1000 ;
localparam SINGLE_PWM_NUM = 1000 ;
localparam CELL_NUM = 100 ;
`endif

logic [15:0] cell_cnt ;
logic [15:0] single_pwm_cnt ;
logic [15:0] total_pwm_cnt ;
logic flag ;
logic cell_done ;
logic single_pwm_done ;
logic total_pwm_done ;

assign cell_done = (cell_cnt == CELL_NUM - 1);
assign single_pwm_done = (single_pwm_cnt == SINGLE_PWM_NUM - 1);
assign total_pwm_done = (total_pwm_cnt == TOTAL_PWM_NUM - 1);

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
cell_cnt <= 'h0;
else if(cell_done)
cell_cnt <= 'h0;
else
cell_cnt <= cell_cnt + 'h1;

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
single_pwm_cnt <= 'h0;
else if(cell_done) begin
if(single_pwm_done)
single_pwm_cnt <= 'h0;
else
single_pwm_cnt <= single_pwm_cnt + 'h1;
end

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
total_pwm_cnt <= 'h0;
else if(cell_done && single_pwm_done) begin
if(total_pwm_done)
total_pwm_cnt <= 'h0;
else
total_pwm_cnt <= total_pwm_cnt + 'h1;
end

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
flag <= 1'b1;
else if(cell_done && single_pwm_done && total_pwm_done)
flag <= ~flag;


always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
led <= 1'b0;
else if(single_pwm_cnt <= total_pwm_cnt)
led <= flag;
else
led <= ~flag;

endmodule

下面这个代码就是在进行不同模式灯的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
module cfg_led(
input logic clk ,
input logic rst_n ,
input logic [3:0] led_pattern ,
input logic blink_led ,
input logic breath_led ,
input logic [3:0] flow_led ,
output logic [3:0] led
);

always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
led <= 1'b0;
else begin
case(led_pattern)
4'd0 : led <= {4{1'b0}};
4'd1 : led <= {4{1'b1}};
4'd2 : led <= {4{blink_led}};
4'd3 : led <= {4{breath_led}};
4'd4 : led <= flow_led;
4'd5 : led <= {1'b0,1'b1,blink_led,breath_led};
4'd6 : led <= {blink_led,breath_led,blink_led,breath_led};
default : led <= flow_led;
endcase
end

endmodule

这是一个LED 子系统的顶层架构文件,扮演着“灯光总管”的角色。它通过实例化三个底层驱动模块(u_blink_ledu_breath_ledu_flow_led),让闪烁、呼吸、流水这三种效果生成器在后台并行工作;同时实例化了一个核心选择器 u_cfg_led_inst,负责根据上级输入的控制指令 led_pattern,从上述三个正在运行的信号源中筛选出特定的效果,最终驱动物理 LED 端口,完美实现了效果生成与控制选择的解耦

LED的顶层文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
module led_top(
input logic clk ,
input logic rst_n ,
input logic [3:0] led_pattern ,
output logic [3:0] led
);

logic blink_led ;
logic breath_led ;
logic [3:0] flow_led ;
blink_led u_blink_led_inst(
.clk (clk ),
.rst_n (rst_n ),
.led (blink_led )
);

breath_led u_breath_led_inst(
.clk (clk ),
.rst_n (rst_n ),
.led (breath_led )
);

flow_led u_flow_led_inst(
.clk (clk ),
.rst_n (rst_n ),
.led (flow_led )
);

cfg_led u_cfg_led_inst(
.clk (clk ),
.rst_n (rst_n ),
.led_pattern (led_pattern ),
.blink_led (blink_led ),
.breath_led (breath_led ),
.flow_led (flow_led ),
.led (led )
);

endmodule

5、按键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
`timescale 1ns / 1ps
//--------------------------------------------------------------------------------
// 模块名称: debounce
// 功能描述:
// 按键消抖模块。利用计数器原理,滤除机械按键输入时的抖动毛刺。
// 只有当输入信号保持稳定达到指定时间(默认20ms)后,输出才会改变。
//--------------------------------------------------------------------------------

module debounce(
input logic clk, // 系统时钟 (假设为 100MHz)
input logic rst_n, // 系统复位 (低电平有效)
input logic key_in, // 物理按键输入 (存在抖动)
output logic key_out // 消抖后的输出 (干净信号)
);

//--------------------------------------------------------------------------------
// 参数定义
//--------------------------------------------------------------------------------
// 20ms 的计数值。计算公式: Time / Clk_Period
// 20ms / 10ns (100MHz) = 2,000,000
localparam CNT_20MS = 2000000;

//--------------------------------------------------------------------------------
// 内部信号
//--------------------------------------------------------------------------------
logic key_in_dly; // 输入信号的延迟打拍 (用于检测边缘变化)
logic [31:0] cnt; // 消抖计数器 (32位宽)

//--------------------------------------------------------------------------------
// 逻辑块 1: 输入打拍 (Input Registering)
//--------------------------------------------------------------------------------
// 目的: 记录“上一个时钟周期”的按键状态
always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
key_in_dly <= 1'b1; // 复位时默认按键未按下 (假设低电平有效)
else
key_in_dly <= key_in; // 每个时钟周期更新一次状态

//--------------------------------------------------------------------------------
// 逻辑块 2: 稳定性计时器 (Stability Timer)
//--------------------------------------------------------------------------------
// 核心逻辑:
// 1. 如果信号在抖动 (当前值 != 上一次的值),立即清零计数器。
// 2. 只有信号“老实”地保持不变,计数器才会累加。
always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 'h0;
else if(key_in != key_in_dly) // 检测到边沿变化 (说明有抖动或按键动作)
cnt <= 'h0; // 计数器清零,重新开始计时
else if(cnt == CNT_20MS - 1) // 如果计数器已经数满了 20ms
cnt <= cnt; // 保持计数器不变 (饱和)
else
cnt <= cnt + 'h1; // 信号稳定,且未满20ms,继续计数

//--------------------------------------------------------------------------------
// 逻辑块 3: 输出更新 (Output Update)
//--------------------------------------------------------------------------------
// 目的: 只有当信号稳定时间足够长 (计数器满了),才更新输出
always_ff @(posedge clk or negedge rst_n)
if(!rst_n)
key_out <= 1'b1; // 复位默认输出高电平
else if(cnt == CNT_20MS - 1) // 只有当由于信号稳定导致计数器数满时
key_out <= key_in_dly; // 才认可这个按键状态,更新输出
// 否则保持 key_out 不变 (滤除短于 20ms 的所有毛刺)

endmodule

这个key_out <= key_in_dly,这句代码当检测到按下时,输出为0,不按下的时候,检测为1.

ESC 关闭 | 导航 | Enter 打开
输入关键词开始搜索