北航计组-P1课下

就是写篇博客记录下自己的做题过程&心得,并非题解,如果寻求速通题解的朋友可以移步讨论区或者其他dalao的博客哟 😄

Q1 Splitter

题面:使用 Verilog 搭建一个 32位 Splitter , 给定一个 32位 的二进制数作为输入,将其划分为 四个 8位 的二进制数作为输出
纯复习题,就是按部就班地将input的 四个 8位 二进制数作为 四个 输出的驱动即可
可悲的是,本人第一遍竟然把 32 位的 Input写成了input 数组 👇

1
2
3
4
# wrong
input A [31:0],
# correct
input [31:0] A,

果然,还是太久不写verilog,生疏了(其实就是菜😢

Q2 ALU

题目

使用 Verilog 搭建一个 32 位六运算 ALU 并提交。具体模块端口定义如下:(看到这个表格才发现我Q1的弱智错误似乎就是因为题目的描述是A [31:0] (疯狂找借口) 😝

模块功能如下:

知识复习:

在这里我们需要复习逻辑右移和算术右移的区别:

逻辑右移不考虑符号位,右移一位,左边补零即可。

算术右移需要考虑符号位,右移一位,若符号位为 1 ,在左边补 1 ;否则,补 0 。

例如,8 位二进制数 10111101 分别右移2位。

  • 逻辑右移结果为00101111
  • 算术右移结果为11101111

提示:可以回Pre中的这一小节好好复习了,注意**$signed()**的用法

思路

其他的没什么说的,照搬功能描述,需要注意的是最后一个op, A有符号 ,B无符号

如果直接三目运算符加$signed(A) >>> B,则根据符号原则(最外层确定,向内传播),会把结果变为无符号数,这显然达不到我们想要的结果

因此,采取Pre中的一种推荐方案“对于复杂的表达式避免使用 $signed() ,如果希望使用 $signed() 可以将这一部分抽离出来单独作为一个变量”

意即单独定义一个wire型变量作为$signed(A) >>> B

注意到这点后,本题应该没什么难度了,最后,贴上俺的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module alu (
input [31:0] A,
input [31:0] B,
input [2:0] ALUOp,
output [31:0] C
);
wire [31:0] tmp;
assign tmp = $signed(A) >>> B;
assign C = (ALUOp == 3'b000) ? (A + B) :
(ALUOp == 3'b001) ? (A - B) :
(ALUOp == 3'b010) ? (A & B) :
(ALUOp == 3'b011) ? (A | B) :
(ALUOp == 3'b100) ? (A >> B) :
(ALUOp == 3'b101) ? tmp :
0;

endmodule

结语

本蒟蒻只做了两道目前,先上传到博客主要是测试一下代码块的风格设置是否成功以及其他功能,后续还会更新本篇 😋


updated: 10.10

Q3 EXT

题面

注意

verilog中变量位宽以及进制的定义不可偷懒,否则默认为10进制,32位。

本题直接根据EOp进行位拼接即可,符号位扩展直接拼接“16个最高位”,零扩展直接拼接0即可,需注意 0 要写做 16’b0,否则默认为32位的,贴一下本人的代码

值得注意的是,{16’b0} 也可以写成 {16{1’b0}} ,只是看起来有点“臃肿” 🤔

1
2
3
4
5
6
7
8
9
10
11
12
module ext (
input [15:0] imm,
input [1:0] Eop,
output [31:0] ext
);
assign ext = (Eop == 2'b00) ? {{16{imm[15]}}, imm} :
(Eop == 2'b01) ? {16'b0, imm} :
(Eop == 2'b10) ? {imm, 16'b0} :
(Eop == 2'b11) ? {{14{imm[15]}}, imm, 2'b0} :
32'b0;

endmodule

Q4 Gray

题目

就是设计一个格雷码计数器,具备同步复位功能和使能端,需要了解格雷码的同学可以点击我,顺便给各位推荐一下秘塔搜索比较好用的搜索引擎,可以搜论文(选择“学术”搜索模式),最近也提供了搜索图片功能,无广告,在每次搜索下方还会有相关参考文献,可以点击跳转,超丰富。like this:

步入正题,直接附上题目说明


题目还贴心地附上了格雷码转换表,以及波形图(虽然我没有写testbench,但这并不是一个好习惯,各位还是要尽量写一写tb的 😄)

个人感觉没啥好说的,直接写就是了,附上代码

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
module gray (
input Clk,
input Reset,
input En,
output [2:0] Output,
output Overflow
);
reg [2:0] code = 3'b0;
reg over = 1'b0;
always @(posedge Clk) begin
if(Reset)begin
code <= 3'b0;
over <= 1'b0;
end

else begin
if(En)begin
if(code == 3'b111)begin
over <= 1'b1;
end
code <= code + 3'b1;
end

else begin
code <= code;
end
end
end

assign Output = (code == 3'b000) ? 3'b000 :
(code == 3'b001) ? 3'b001 :
(code == 3'b010) ? 3'b011 :
(code == 3'b011) ? 3'b010 :
(code == 3'b100) ? 3'b110 :
(code == 3'b101) ? 3'b111 :
(code == 3'b110) ? 3'b101 :
(code == 3'b111) ? 3'b100 :
3'b000;
assign Overflow = over;
endmodule

Q5 Expr

题目

思路

题目是几天前写的,忘了(bushi)😢


定义有点递归的味道emmm……
“单个数字[0-9]是F; b. 如果X是F,Y是F,X+Y也是F; c. 如果X是F,Y是F,X*Y也是F”

虽然题目没有明说,但根据题目描述“每个时钟上升沿,状态机从 in 中读入一个ASCII编码的字符。假设读入的第i个字符为ci,则第n个时钟上升沿时,可以拼出一个字符串: s=c1c2….cn 我们需要你此时判断 s 是否符合表达式F的定义”以及波形图,大致可以确定状态机类型:Moore型状态机
读懂题目意思后,其实正确状态就是类似 1 + 2 * 3 + 7 * 9 ,即:
必须先输入一个数字,然后接着输入一个运算符,连续的数字或者运算符都直接pass掉,合法状态要么是一个数字要么是(数字)(运算符)(数字)。故设计5种状态,分别为:

状态说明
state_0空状态,初始状态
state_1错误状态,当非法输入后一直保持此状态,除非reset
state_2只输入了一个数字
state_3后缀输入为(数字)(运算符)
state_4后缀输入为(数字)(运算符)(数字)

后来发现似乎state_2 和 state_4 可以合并,都行
代码:

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
module expr (
input clk,
input clr,
input [7:0] in,
output out
);
reg [2:0] state = 3'b0;
parameter state_0 = 3'b000,
state_1 = 3'b001,
state_2 = 3'b010,
state_3 = 3'b011,
state_4 = 3'b100;

always @(posedge clk or posedge clr) begin
if(clr) begin
state <= state_0;
end

else begin
case (state)
state_0: begin
if(in > 6'd47 && in < 6'd58)begin
state <= state_2;
end
else begin //first input must be number
state <= state_1;
end
end

state_1: begin
state <= state_1;
end

state_2: begin
if(in == 6'd42 || in == 6'd43)begin
state <= state_3;
end
else begin
state <= state_1;
end
end

state_3: begin
if(in > 6'd47 && in < 6'd58) begin
state <= state_4;
end
else begin
state <= state_1;
end
end

state_4: begin
if(in == 6'd42 || in == 6'd43) begin
state <= state_3;
end
else begin
state <= state_1;
end
end

default: state <= state_0;
endcase
end
end

assign out = (state == state_4) ? 1'b1 :
(state == state_2) ? 1'b1 : 1'b0;
endmodule

Q6 BlockChecker

本题为附加题,通过与否不计入P1课下通过条件

题目

分析

其实就是把之前数据结构的一道括号匹配题魔改了,核心思路差不多,通过一个flag来标志匹配状态,读入 Begin(左括号)时 flag 加 1 ,End(右括号)时flag - 1,最后判断 flag 是否为 0 来进行输出同时如果输入 begin 之后,不输入空格,而是输入其他字符,则应该把flag相应地加(减)回去。
值得注意的是:本题是一道本题依旧是 Moore 状态机,因为观察波形输出 result 只在 clk 上升沿变化 !

对于输入,如果未匹配的 end 出现在了 begin 前面, 则输入已经非法,此后不管输入如何,都输出 0 (除非reset),那么我们便可以设置一个单独的状态来保存这一状态, 当flag < 0 时直接就“锁”在这个状态里。

同时,鼓励大家使用parameter 来声明_状态_这一常量。

多的懒得说了,代码里有注释,直接上代码😁😁😁

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
module BlockChecker (
input clk,
input reset,
input [7:0] in,
output result
);
reg [3:0] state = 4'b0001; //store status
reg [31:0] match = 32'b0; //store if_match

parameter st_0 = 4'b0000, //null or initial
st_1 = 4'b0001, //input " "
st_2 = 4'b0010, //input "b"
st_3 = 4'b0011, //input "be"
st_4 = 4'b0100, //input "beg"
st_5 = 4'b0101, //input "begi"
st_6 = 4'b0110, //input "begin"
st_7 = 4'b0111, //input other words,such as "hello"
st_8 = 4'b1000, //input "e"
st_9 = 4'b1001, //input "en"
st_10 = 4'b1010; //input "end"

always @(posedge clk or posedge reset) begin
if(reset) begin
state <= st_1;
match <= 32'b0;
end

else begin
case (state)
st_0: begin
state <= st_0;
end

st_1: begin
if($signed (match) < $signed(32'b0)) begin
state <= st_0;
end
else begin
if(in == "b" || in == "B") begin
state <= st_2;
end
else if(in == "e" || in == "E") begin
state <= st_8;
end
else if(in == " ")begin
state <= st_1;
end
else begin
state <= st_7;
end
end

end

st_2: begin
if(in == "e" || in == "E") begin
state <= st_3;
end
else begin
state <= st_7;
end
end

st_3: begin
if(in == "g" || in == "G") begin
state <= st_4;
end
else if(in == " ")begin
state <= st_1;
end
else begin
state <= st_7;
end
end

st_4: begin
if(in == "i" || in == "I") begin
state <= st_5;
end
else if(in == " ")begin
state <= st_1;
end
else begin
state <= st_7;
end
end

st_5: begin
if(in == "n" || in == "N") begin
match <= match + 32'b1;
state <= st_6;
end
else if(in == " ")begin
state <= st_1;
end
else begin
state <= st_7;
end
end

st_6: begin
if(in == " ") begin
state <= st_1;
end
else begin
match <= match - 32'b1;
state <= st_7;
end
end

st_7: begin
if(in == " ") begin
state <= st_1;
end
else begin
state <= st_7;
end
end

st_8: begin
if(in == "n" || in == "N") begin
state <= st_9;
end
else if(in == " ")begin
state <= st_1;
end
else begin
state <= st_7;
end
end

st_9: begin
if(in == "d" || in == "D") begin
match <= match - 32'b1;
state <= st_10;
end
else if(in == " ")begin
state <= st_1;
end
else begin
state <= st_7;
end
end

st_10: begin
if(in == " ") begin
state <= st_1;
end
else begin
match <= match + 32'b1;
state <= st_7;
end
end

default: state <= st_0;

endcase
end
end

assign result = (match == 32'b0) ? 1'b1 : 1'b0;

endmodule

最后提一嘴,本人一开始不知道verilog中可以直接双引号加字母,like “A”,来表示字母的acii码,,也定义了一堆ascii常量,悲!😭😭😭

1
2
3
4
5
6
7
parameter in_b = 8'd66, in_B = 8'd98,
in_d = 8'd68, in_D = 8'd100,
in_e = 8'd69, in_E = 8'd101,
in_g = 8'd71, in_G = 8'd103,
in_i = 8'd73, in_I = 8'd105,
in_n = 8'd78, in_N = 8'd110,
in_space = 8'd32;

北航计组-P1课下
http://pzhwuhu.github.io/2024/10/06/P1课下/
本文作者
pzhwuhu
发布于
2024年10月6日
更新于
2025年4月18日
许可协议