0%

数字逻辑实验笔记

这一部分主要是verilog语法,比较简单。

两种声明方式

1
2
3
4
5
module AND4(a, b, out);
input [3:0] a, b;
output [3:0] out;
assign out = a & b;
endmodule
1
2
3
module AND4(input [3:0] a, input [3:0] b, output [3:0] out);
assign out = a & b;
endmodule

数据类型

掌握wire和reg即可。

wire就是对应着电路里的线。reg就是对应着电路里的寄存器。

常量用parameter定义,例如parameter width = 3; 但是常量也可以重定义,例如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
module adder(a, b, sum);
parameter time_delay = 5, time_count = 10;
...
endmodule

module top;
wire [2:0] a1, b1;
wire [3:0] sum1;
wire [3:0] a2, b2;
wire [3:0] sum2;
adder #(4, 8) AD1(a1, b1, sum1); //time_delay=4, time_count=8
adder #(12) AD2(a2, b2, sum2); //time_delay=12, time_count=10
endmodule

数据的表达方式

<位宽>'<进制><数字>:4'b0101 / 4‘h5

<进制><数字>:位宽一般默认是32位。h5

<数字>:位宽默认32位,进制默认为十进制。5

拼接运算符

{a, b[3:0], w, 3'b101} 等价于 {a, b[3], b[2], b[1], b[0], w, 1'b1, 1'b0, 1'b1}

{4{w}} 等价于 {w, w, w, w}

{b, 3{a, b}} 等价于 {b, a, b, a, b, a, b}

赋值语句

assign只能对wire类型赋值。assign的作用通常就是把reg的结果用一个wire接收。

initial里对reg变量赋初值。

always里的=(阻塞赋值)/<=(非阻塞赋值)对always块里的reg类型赋值。

always语句

always @ (clk) //只要clk发生变化就触发 always @ (posedge clk) //clk上升沿触发(从0->1触发)

always @ (negedge clk) //clk下降沿触发(从1->0触发)

always @ (*) //always块内任何输入信号变化了就触发

条件语句

条件语句只能用于always或initial内部。只需要掌握if-else和case语句即可。

if-else语句:跟C++一样,只不过多条语句时将{}换成begin-end

case语句:有case/casex/casez可以选择。casex就是分支表达式中可以有?作为通配符。

循环语句

一般循环语句都是用来写仿真文件的。因为循环次数不确定的循环语句是不能被综合的!

所以一般用循环语句来写源代码都是用for循环


接下来的内容就是实验了,实验代码自己手撸的。而且只学了基本语法就开干了,所以代码自我感觉很丑不优美。

实验一

1
2
3
4
5
6
7
8
module demo02_verilog #(parameter W1 = 1, W2 = 3) (
input [(W1 - 1) : 0] sw15, sw14,
input [(W2 - 1) : 0] in1, in2, in3, in4,
output [(W2 - 1) : 0] ld
);
//错误代码:assign ld = (~sw15 & ~sw14 & in1) | (~sw15 & sw14 & in2) | (sw15 & ~sw14 & in3) | (sw15 & sw14 & in4);
assign ld = sw15 ? (sw14 ? in4 : in3) : (sw14 ? in2 : in1);
endmodule

实验二

  • top.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module top(a, b, t, e, l1, l2, l3, clk, reset, an, led);
input [3:0] a, b;
output t, e;
output l1, l2, l3; //控制负数亮灯
wire [3:0] out;

input clk, reset;
output [3:0] an;
output [6:0] led;

add #(4) f1(.a(a), .b(b), .out(out), .t(t), .e(e));
show(.clk(clk), .reset(reset),
.hex0(out), .hex2(b), .hex3(a),
.an(an), .sseg(led), .l1(l1), .l2(l2), .l3(l3));
endmodule
  • add.v
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
//进位不影响补码运算的正确性,溢出影响正确性,溢出的本质就是超过了位宽能表达的范围
module add (a, b, out, t, e);
parameter W = 32;
input [W-1:0] a, b;
output [W-1:0] out;
output t, e;

reg [W-1:0] a_, b_; //补码
reg [W-1:0] out_; //运算后的补码
reg [W-1:0] out;
reg [31:0] i;
reg t = 0, e = 0;
reg tt = 0;
always @ (*) begin
//都转为补码
a_ = a;
b_ = b;
if (a_[W-1] == 1) begin
for (i = 0; i < W - 1; i = i + 1) a_[i] = ~a[i];
t = 0;
for (i = 0; i < W; i = i + 1) begin
if (i == 0) begin
tt = a_[i];
a_[i] = a_[i] ^ 1 ^ t;
t = (tt & 1) | (t & (tt | 1));
end
else begin
tt = a_[i];
a_[i] = a_[i] ^ 0 ^ t;
t = (tt & 0) | (t & (tt | 0));
end
end
end
if (b_[W-1] == 1) begin
for (i = 0; i < W - 1; i = i + 1) b_[i] = ~b[i];
t = 0;
for (i = 0; i < W; i = i + 1) begin
if (i == 0) begin
tt = b_[i];
b_[i] = b_[i] ^ 1 ^ t;
t = (tt & 1) | (t & (tt | 1));
end
else begin
tt = b_[i];
b_[i] = b_[i] ^ 0 ^ t;
t = (tt & 0) | (t & (tt | 0));
end
end
end
//俩补码运算
t = 0;
for (i = 0; i < W; i = i + 1) begin
out_[i] = a_[i] ^ b_[i] ^ t;
t = (a_[i] & b_[i]) | (t & (a_[i] | b_[i]));
end
//检测是否溢出
if (a_[W-1] == 0 && b_[W-1] == 0 && out_[W-1] == 1) e = 1;
else if (a_[W-1] == 1 && b_[W-1] == 1 && out_[W-1] == 0) e = 1;
else e = 0;
//将补码转为原码
if (out_[W-1] == 1) begin
//变反码
for (i = 0; i < W; i = i + 1) begin
a_[i] = out_[i];
b_[i] = 1;
end
tt = 0;
for (i = 0; i < W; i = i + 1) begin
out_[i] = a_[i] ^ b_[i] ^ tt;
tt = (a_[i] & b_[i]) | (tt & (a_[i] | b_[i]));
end
//变原码
for (i = 0; i < W - 1; i = i + 1) out_[i] = ~out_[i];
end
//赋值
for (i = 0; i < W; i = i + 1) out[i] = out_[i];
end
endmodule
  • show.v
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
module show(clk, reset, hex0, hex1, hex2, hex3, an, sseg, l1, l2, l3);
input clk, reset;
input [3:0] hex0, hex1, hex2, hex3; //分别存储4个灯要显示的数字(0-15)
output [3:0] an; //控制每个灯是否工作,0为工作
output [6:0] sseg; //存储数字在数码管上的表示
output l1, l2, l3; //控制负数亮灯

assign l1 = hex3[3];
assign l2 = hex2[3];
assign l3 = hex0[3];

localparam N = 18;
reg [N-1:0] regN;
always @ (posedge clk, posedge reset) begin
if(reset) regN <= 0;
else regN <= regN + 1;
end

reg [3:0] an;
reg [3:0] hex_in; //存储当前要展示的数字
always @ (*) begin
case(regN[N-1 : N-2])
2'b00: begin
an = 4'b1110;
hex_in = hex0;
end
2'b01: begin
an = 4'b1101;
hex_in = hex1;
end
2'b10: begin
an = 4'b1011;
hex_in = hex2;
end
default: begin
an = 4'b0111;
hex_in = hex3;
end
endcase
end

reg [6:0] sseg;
always@(posedge clk) begin
case(hex_in[2:0])
4'h0: sseg[6:0] = 7'b0000001;
4'h1: sseg[6:0] = 7'b1001111;
4'h2: sseg[6:0] = 7'b0010010;
4'h3: sseg[6:0] = 7'b0000110;
4'h4: sseg[6:0] = 7'b1001100;
4'h5: sseg[6:0] = 7'b0100100;
4'h6: sseg[6:0] = 7'b0100000;
4'h7: sseg[6:0] = 7'b0001111;
endcase
end

endmodule

实验三

  • top.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module top(clk, A, B, sum, t);
input clk;
input [31:0] A, B;
output reg [31:0] sum;
output reg t;

reg [15:0] Lsm_d1;
reg [15:0] Aup_d1, Bup_d1;
reg Carry_d1;

always @ (clk) begin
{Carry_d1, Lsm_d1} = A[15:0] + B[15:0];
Aup_d1 = A[31:16];
Bup_d1 = B[31:16];
end
always @ (clk) begin
sum[15:0] = Lsm_d1;
{t, sum[31:16]} = Aup_d1 + Bup_d1 + Carry_d1;
end
endmodule

实验四

  • top.v
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
// top 模块:整合时钟分频器和交通灯控制器
module top(
input clk, // 输入:主时钟信号
input rst_n, // 输入:复位信号
output [2:0] light_NS, // 输出:南北方向交通灯状态
output [2:0] light_EW // 输出:东西方向交通灯状态
);

wire clk_out; // 内部线网:用于从时钟分频器传递分频后的时钟

// 实例化时钟分频器模块
clockdivider u1(
.clk(clk),
.rst_n(rst_n),
.clk_out(clk_out)
);
// 实例化交通灯控制模块
traffic_light u2(
.clk(clk_out), // 使用分频后的时钟
.rst_n(rst_n),
.light_NS(light_NS), // 南北方向交通灯状态
.light_EW(light_EW) // 东西方向交通灯状态
);

endmodule
  • clockdivider.v
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
// clockdivider 模块:用于将输入时钟分频
module clockdivider(
input clk, // 输入:原始的高频时钟信号
input rst_n, // 输入:复位信号
output reg clk_out // 输出:分频后的时钟信号
);

parameter DIVISOR = 32'd1666_6667; // 分频因子
reg [31:0] counter = 32'd0; // 计数器

// 时钟分频逻辑
always @(posedge clk or posedge rst_n) begin
if (rst_n) begin
// 复位时,重置计数器和输出时钟
counter <= 32'd0;
clk_out <= 1'b0;
end else begin
// 计数器达到分频因子时,翻转输出时钟并重置计数器
if (counter == DIVISOR - 1) begin
clk_out <= ~clk_out;
counter <= 32'd0;
end else begin
// 否则,计数器递增
counter <= counter + 1;
end
end
end

endmodule
  • traffic_light.v
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
// traffic_light 模块:控制交通灯状态
module traffic_light(
input clk, // 输入:时钟信号
input rst_n, // 输入:复位信号
output reg [2:0] light_NS, // 输出:南北方向交通灯状态
output reg [2:0] light_EW // 输出:东西方向交通灯状态
);

// 状态定义
parameter [2:0] GREEN_NS_RED_EW = 3'b000,
YELLOW_NS_RED_EW = 3'b001,
RED_NS_RED_EW_1 = 3'b010,
RED_NS_GREEN_EW = 3'b011,
RED_NS_YELLOW_EW = 3'b100,
RED_NS_RED_EW_2 = 3'b101;

reg [2:0] state = GREEN_NS_RED_EW; // 当前状态
reg [3:0] counter = 4'b0000; // 计时器

// 时钟边沿触发的逻辑
always @(posedge clk or posedge rst_n) begin
if (rst_n) begin
// 复位逻辑
state <= GREEN_NS_RED_EW;
counter <= 4'b0000;
end else begin
// 状态转换逻辑
case (state)
GREEN_NS_RED_EW: begin
if (counter == 4'b1111) begin // 15时钟周期
state <= YELLOW_NS_RED_EW;
counter <= 4'b0000;
end
end
YELLOW_NS_RED_EW: begin
if (counter == 4'b0011) begin // 3时钟周期
state <= RED_NS_RED_EW_1;
counter <= 4'b0000;
end
end
RED_NS_RED_EW_1: begin
if (counter == 4'b0011) begin // 3时钟周期,短暂的全红状态
state <= RED_NS_GREEN_EW;
counter <= 4'b0000;
end
end
RED_NS_GREEN_EW: begin
if (counter == 4'b1111) begin // 15时钟周期
state <= RED_NS_YELLOW_EW;
counter <= 4'b0000;
end
end
RED_NS_YELLOW_EW: begin
if (counter == 4'b0011) begin // 3时钟周期
state <= RED_NS_RED_EW_2;
counter <= 4'b0000;
end
end
RED_NS_RED_EW_2: begin
if (counter == 4'b0011) begin // 3时钟周期
state <= GREEN_NS_RED_EW;
counter <= 4'b0000;
end
end
endcase

// 计数器递增
counter <= counter + 1;

// 根据当前状态更新交通灯输出
case (state)
GREEN_NS_RED_EW: begin
light_NS <= 3'b010; // 南北绿灯
light_EW <= 3'b100; // 东西红灯
end
YELLOW_NS_RED_EW: begin
light_NS <= 3'b001; // 南北黄灯
light_EW <= 3'b100; // 东西红灯
end
RED_NS_RED_EW_1: begin
light_NS <= 3'b100; // 南北红灯
light_EW <= 3'b100; // 东西红灯
end
RED_NS_GREEN_EW: begin
light_NS <= 3'b100; // 南北红灯
light_EW <= 3'b010; // 东西绿灯
end
RED_NS_YELLOW_EW: begin
light_NS <= 3'b100; // 南北红灯
light_EW <= 3'b001; // 东西黄灯
end
RED_NS_RED_EW_2: begin
light_NS <= 3'b100; // 南北红灯
light_EW <= 3'b100; // 东西红灯
end
endcase
end
end

endmodule

大作业

1705202189606
1705202209567
  • top.v
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
module top(clk, reset, btn, an, sseg, current_floor, goal_floor);
input clk;
input reset;
output reg [3:0] an;
output reg [6:0] sseg;
reg [5-1:0] hex_in; //存储要显示的楼层数字

input [5-1:0] btn; // 按钮输入
output [5-1:0] current_floor; // 当前楼层(例如01000)
output [5-1:0] goal_floor; // 呼叫的楼层(例如01000)

localparam N = 18; //分频系数(50MHZ/2^16)
reg [N-1:0] regN; //高两位作为控制信号,低16位为计数器,对时钟进行分频
always @ (posedge clk, posedge reset) begin
if(reset) regN <= 0;
else regN <= regN + 1;
end

solve u1(.clk(clk),
.rst(reset),
.btn(btn),
.key_pulse(),
.current_floor(current_floor),
.goal_floor(goal_floor)
);

always @ * begin
case(regN[N-1 : N-2])
2'b00: begin
an = 4'b0111; //选中第1个数码管
hex_in = current_floor;
end
2'b11:begin
an = 4'b1110;//选中第四个数码管
hex_in = goal_floor;
end
endcase
end

always@ * begin
case(hex_in)
5'b00000: sseg[6:0] = 7'b0000001;
5'b00001: sseg[6:0] = 7'b1001111;
5'b00010: sseg[6:0] = 7'b0010010;
5'b00100: sseg[6:0] = 7'b0000110;
5'b01000: sseg[6:0] = 7'b1001100;
5'b10000: sseg[6:0] = 7'b0100100;
default: sseg[6:0] = 7'b1111111;
endcase
end

endmodule
  • solve.v
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
module solve(clk, rst, btn, key_pulse, current_floor, goal_floor);
input clk;
input rst;
input [5-1:0] btn;
output [5-1:0] current_floor;
output [5-1:0] goal_floor;


wire [5-1:0] key_edge; // 按键松开时,其为1,使得cnt清零,产生延时
reg [5-1:0] key_lat; // 存储上一个触发时的按键值
reg [5-1:0] key_now; // 存储当前时刻触发的按键值
always @ (posedge clk or negedge rst) begin
if (rst) begin
key_now <= {5{1'b1}};
key_lat <= {5{1'b1}};
end
else begin
key_now <= btn;
key_lat <= key_now;
end
end
assign key_edge = key_lat & (~key_now);

//产生20ms延时,当检测到key_edge有效是计数器清零开始计数
reg [17:0] cnt;
always @ (posedge clk or negedge rst) begin
if(rst) cnt <= 18'h0;
else if(key_edge) cnt <= 18'h0;
else cnt <= cnt + 1'h1;
end

// 用f1、f2俩变量来控制实现"只用按一次按钮就可以,不用一直按"的效果
reg [5-1:0] f1;
reg [5-1:0] f2;
// 延时更新f1
always @ (posedge clk or negedge rst) begin
if (rst) f1 <= {5{1'b0}};
else if (cnt==18'h3ffff) f1 <= btn;
end
// f2随时保持f1的节奏
always @ (posedge clk or negedge rst) begin
if (rst) f2 <= {5{1'b0}};
else f2 <= f1;
end

// 一开始俩都是0000,所以key_pulse不会改变
// 一直按住某个按键的话,就会出现f2 = 0, f1 = 1的情况,此时key_pulse会翻转为1
// 如果按住某个按键,然后松开的话,那么会使得cnt重新为0。也就是f1 = 1的情况会延时一段时间,此时f2趁机更新为1。
// 从而延时这段时间,不会使得key_pulse翻转
// 当延时到了,就会出现,key_sec更新为0,f2 = 1的情况。此时也不会使得key_pulse翻转
// 然后重新回到俩都是0的情况
// 当再按一次时,就会出现如上的情况,从而实现"取消"的功能
output reg [5-1:0] key_pulse; // 处理后的按键状态
always @ (posedge clk or negedge rst) begin
if (rst) key_pulse <= {5{1'b0}};
else begin
if(~f2[4] & f1[4]) key_pulse[4] <= ~key_pulse[4];
if(~f2[3] & f1[3]) key_pulse[3] <= ~key_pulse[3];
if(~f2[2] & f1[2]) key_pulse[2] <= ~key_pulse[2];
if(~f2[1] & f1[1]) key_pulse[1] <= ~key_pulse[1];
if(~f2[0] & f1[0]) key_pulse[0] <= ~key_pulse[0];
end
end


get_floor u2(.clk(clk),
.rst(rst),
.key_pulse(key_pulse),
.current_floor(current_floor),
.goal_floor(goal_floor)
);

endmodule
  • get_floor.v
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
module get_floor(clk, rst, key_pulse, current_floor, goal_floor);
input clk;
input rst;
input [4:0] key_pulse;
output reg [4:0] current_floor;
output reg [4:0] goal_floor;

initial begin
goal_floor = 5'b00001;
current_floor = 5'b00001;
end

// 时钟分频器:每过1s,clk_out=~clk_out。实现"每一秒钟,电梯移动一层"
reg [30:0] cnt = 30'b0;
parameter M = 100000000; //1s=1000000000ns
reg clk_out = 0;
always @ (posedge clk) begin
if(rst) clk_out = 0;
if (cnt == M-1) begin
clk_out = ~clk_out;
cnt = 0;
end
else begin
clk_out = 0;
cnt = cnt + 1'd1;
end
end

// 更新goal_floor
always @ (*) begin
if(rst) goal_floor = 5'b00001;
else goal_floor = key_pulse;
end
// 更新current_floor
always @ (posedge clk_out or posedge rst) begin
if(rst) current_floor = 5'b00001;
else if(goal_floor != 5'b00000) begin
if(current_floor < goal_floor) //电梯所在楼层低于目标楼层则上升
current_floor = {current_floor[3:0], current_floor[4]};
else if(current_floor > goal_floor) //电梯所在楼层高于目标楼层则下降
current_floor = {current_floor[0], current_floor[4:1]};
end
end

endmodule