依旧是在遇到了大量突发情况下惊险完成了P1,于是想着将我的想法与遇到的问题写下,期望能够对大家有些许的启发www~


T1:P1_L1_dotProduct_2023

题解:

法一:暴力大法好

应该有不少同学和我一样,考试中不确定for怎么写,直接ctrl+c ctrl+v写了32位的🤪🤪

硬要说有什么优化的话…vector_a和vector_b字数太长了,看着有点烦容易打错字,可以自己定义两个32位wire替代~

1
2
3
4
5
6
7
8
9
wire [31:0] a;
wire [31:0] b;
assign a = vector_a;
assign b = vector_b;
assign result =
(a[0] & b[0]) +
(a[1] & b[1]) +
//......
(a[31] & b[31]);

当然,你也可以在C或者Python或者JAVA(或者verilog)中打表来减少复制时间

1
2
for (int i = 0;i < 32;i++) 
cout << "(a[" << i << "] & b[" << i << "]);" << endl;

法二:for循环

在Verilog里写for循环优雅地解决问题当然是我们最提倡的方式

一般来说,verilog里的for循环可以这么写

1
2
3
4
5
6
7
integer i;
always @(*) begin
//preliminary
for (i = 0;i < max_i;i = i + 1) begin
//do something
end
end

将进行位运算的步骤放进for循环里,很轻松就可以求解问题


T2:P1_L4_coloring_2023

题解:

本题有两种设计状态的方式:

  1. 用一个变量存储当前状态

    当前序列 编码
    无颜色 000
    001
    红红 010
    绿 011
    绿绿 100
    101
    蓝蓝 110
  2. 用两个变量存储当前状态

    当前颜色 当前数量
    无颜色 0
    1
    2
    绿 1
    2
    1
    2

从状态数上,两种方法差不多,但在实际编程中,第二种表示方法由于可以用if合并一些情况的处理方式,也许会稍微快一点

下面以方法二模式列出代码

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
reg [1:0] now; //当前颜色
reg [1:0] cnt; //当前数量
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
//preliminary
end
else begin
case (cnt)
0 : begin
//do something
end
1 : begin
if (color == now) begin
//do something
end
else if ( (now ^ color) == 1) begin //判断是否红绿相接,下同
//do something
end
else begin
//do something
end
end
2 : begin
if (color == now) begin
//do something
end
else if ( (now ^ color) == 1) begin
//do something
end
else begin
//do something
end
end
default : begin
//do something
end
endcase
end
end

有以下两点需要注意一下:

  1. 本题为异步复位,且为低电平有效,假如实在想不起来 negedge,就只能试试用组合逻辑实现清零操作了(据说有佬成功了orz)
  2. 在初始化的时候,不建议把颜色清成0,否则当下一个输入1的时候电路就会出错。解决办法为特判一下或者一开始就把颜色清成2,这样就不会和任何输入冲突。(因为这个挂了好几次~😿)

T3:numberNest

题意:

定义两个相同数字之间的部分为“数字匹配”,对于数字匹配,有以下要求:

  1. 一个数字的匹配中的数字必须比这个数大,如123321是合理的,而121121是不合理的
  2. 一个数字匹配的下一个数字只能比这个数字大1,如1234444321是合理的,而1331是不合理的

输入:串行输入的数字序列

输出:当前输入的序列是否合理,合理输出0;不合理输出1,并将当前序列清空

(由于记忆问题,题目描述可能有略微区别,还请大家多多谅解www~)

题解:

本题依旧有两个解法:

法一:状态机大法

题目对数据限制了最多为5,据此可以列出状态机

相信一定会有别的大佬详细讲解这种方法的,我就不献丑啦🎶~

法二:两个if法

定义一个状态state,为当前正在进行匹配的数字,如123的state为3,122的state为1,空串的state为0

不难发现,在任何情况下,我们的合理输入(用in表示)只有两种:

  1. in = state + 1

    序列开始了下一个匹配,需要将state加一

  2. in = state

    序列结束了当前匹配,需要将state减一

(这么说可能稍微有点抽象,大家可以自己构造几个数据跑一跑)

相反,只要不满足上面的情况,就说明序列不合理

我们可以简单地写出代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
reg [2:0] state;
always @ (posedge clk or negedge rst_n) begin
if (!rst_n) begin
check <= 0;
state <= 0;
end
else begin
if (in == state + 1) begin
check <= 0;
state <= state + 1;
end
else if (in == state) begin
check <= 0;
state <= state - 1;
end
else begin
check <= 1;
state <= 0;
end
end
end

这种方法的优点在于代码比较短,可拓展性比较好(指对数据范围的依赖小)


总结:

本次考试的三个题目,并没有简单地考查状态机或组合电路等基本知识,而是要求考生对Verilog编程语言具有较深刻的理解和熟练掌握,才能较为轻松地解决。

同时,本次考试加大了对测试块(TB)编写的要求,对于P4及以后的练习,很可能会出现不编写测试块就无法解决的问题。愿我们都能在此之前武装到牙齿,能够从容面对即将到来的挑战~