计组P1课下作业总结
P1.Q1 Verilog实现splitter
- 题面:
使用 Verilog 搭建一个32位 Splitter , 给定一个32位的二进制数作为输入,将其划分为四个8位的二进制数作为输出。 - 端口定义:
- 思路:
就是每次使输入A向右逻辑移位8位,然后再取移位完以后的低8位即为对应输出。
取低8位的方法我采用的是和8’b11111111进行按位与运算,输出的即为低8位。1
2
3
4assign O4 = A & (8'b11111111);
assign O3 = (A >> 8) & (8'b11111111);
assign O2 = (A >> 16) & (8'b11111111);
assign O1 = A >> 24;
P1.Q2 Verilog实现ALU
- 题面:
使用 Verilog 搭建一个 32 位六运算 ALU 并提交。 - 端口定义:
- 模块功能定义:
- 思路:对ALUOp输入进行判断,然后进行相应的计算,最后输出即可。
- 主要问题:
首先就是判断输出,由于本题是组合逻辑,所以如果不用reg和always的组合,就不能使用if,case,while等等(只能在顺序块中使用)而应该使用三目运算符。
其次就是进行算数位移,不能直接把$signed(A) >>> B
写在三目运算中,因为此之前还会有没有被声明为有符号数的A,导致此声明无效,最终还是逻辑位移。所以需要先对其计算,然后输出时直接输出计算值。最后就是在tb中进行测试时,如果要测试算数右移,记得一定要使用三十二位符号数带入,如果你输入的符号数小于三十二位,那么高位补0后相当于将其又强制转化为了正数,此时依然是逻辑右移效果。1
2
3
4
5
6
7
8
9wire [31:0] shift;
assign shift = $signed(A) >>> B;
assign C = (ALUOp == add) ? A + B :
(ALUOp == sub) ? A - B :
(ALUOp == and_gate) ? A & B :
(ALUOp == or_gate) ? A | B :
(ALUOp == logic_right) ? A >> B :
(ALUOp == signed_right) ? shift :
0;
P1.Q3 Verilog实现EXT
- 题面:
EXT为扩展单元,其主要功能是完成将输入到其中的16位数据进行符号扩展、零扩展以及将输入的16位数加载到高位等操作。 - 端口定义:
- 功能定义:
- 思路:
利用位拼接运算符{}即可,根据题意拼接0或者最高位即可。
1 |
|
需要注意的点:不能写16{0}这样的式子,因为在你不指定0的限制时,默认是16位10进制数,此时拼接时相当于拼接了256位,明显错误。而应该写16{1‘b0}表示一位二进制0。
P1.Q4 Verilog时序逻辑
- 题面:
利用Verilog进行格雷码计数器的组成。 - 端口定义:
- 实现功能:
1、 在任意一个时钟上升沿到来的时候,如果复位信号有效,则将计数器清零;
2、 每个时钟上升沿到来的时候,如果使能信号有效,计数器的值+1;
3、 在满足1时,即使2的条件满足,也不必执行2;
4、 计数器初值为0;
5、 当计数器的值在+1后出现溢出的情况时,将会回到零,同时从发生溢出的这个时钟上升沿开始,溢出标志位将会持续输出1,直到计数器被清零为止(其余情况下溢出标志位必须为0)。 - 示范波形:
- 格雷码计数方式:
- 思路:
其实就是利用一个寄存器进行计数器状态寄存(当前加到几),然后在输出时利用三目运算符分别将当前寄存器中的状态值(就是对应数)变成对应的格雷码即可。
本题是同步复位,故Reset输入不需要写入敏感表,只需要注意先判断Reset是否为1即可。同时要注意此处溢出输出也需要利用单独的寄存器进行存储,才能在always块中改变(always 中不能有assign)。
状态改变代码:输出代码:(之前已经定义常量)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16always @(posedge Clk) begin
if(Reset == 1) begin
state <= 0;
overflow_memory <= 0;
end
else if(En == 1) begin
if(state + 1 < 8)
state <= state + 1;
else begin
state <= 0;
overflow_memory <= 1;
end
end
else
state <= state;
end1
2
3
4
5assign Output = (state == 0) ? grey_zero :
(state == 1) ? grey_one :
(state == 2) ? grey_two :
(state == 3) ? grey_three :
//……
P1.Q5 Verilog表达式状态机
- 题面:
现在有这样一类表达式F的字符串需要你来验证它们的合法性:
1、表达式F中只含有数字0-9,加号+,乘号* 。
2、表达式F可以按如下的规则产生:
a.单个数字[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的定义。假如s符合F的定义,那么 out 应输出1,否则输出0。
另外,每个 clr 上升沿时,请清零状态;如果 clk 的上升沿时 clr 为 1,也需要清零状态。清零后,上面定义的字符串s也应从空串开始计算。如果s当前是空串,out也应输出0。思路:
本题类似于离散数学中的原子公式,首先画出状态转移图。
然后根据此定义出四个状态(提前用常量规定),然后进行状态转移即可。本题的状态机比较奇特,因为是输入单个表达式而非考虑末尾,所以一旦进入错误状态(以上表格中4种)就无法再成功成为表达式,因此本题中state4进入就无法转移,不需要添加case4。
注意本题是异步复位,所以要把clr信号加入敏感表(注意写法),最后输出即可。状态转移模块:
1 |
|
输出模块:
1 |
|
P1.附加题 语言块检查模拟
题面:
现在需要你用Verilog语言编写一个模拟语句块检查的工具。
为了简化要求,输入由ASCII字母和空格组成。一个或多个连续出现的字母构成一个单词,单词不区分大小写,单词之间由一个或多个空格分隔开。检查工具检查自复位之后的输入中,begin和end是否能够匹配。
匹配规则类似括号匹配:一个begin只能匹配一个end,但是一个匹配的begin必须出现在对应的end之前;允许出现嵌套;最后若出现不能按上述规则匹配的begin或end,则匹配失败。
输入的读取在时钟上升沿进行。例子:
匹配示例:Hello world,begin comPuTer orGANization End。
不匹配示例:eND,beGin study。端口定义:
其余要求:
- 必须严格按照模块的端口定义
- 模块内不要包含任何$display语句 ,以防造成误判
- 我们保证在使用模块前进行复位
- 保证输入的单词数和单词长度均小于2^32
示例波形:
思路:本题的主体思路就是,首先建立两个有限状态机(吃了P0的亏,还是感觉对于不同状态利用不同状态机进行检测更加靠谱,否则可能出现状态重叠等问题很难处理),然后建立一个类似栈的寄存器(我此处用的是reg_state寄存器),每当检测到一个begin时其值加一,表示进入一个begin,检测到一个end时值减一,表示进入一个end。
本题的状态转移比较简单,每进入一个符号就是一个新状态即可。同时要区分输入一个破坏单词的字符(比如begi后输入非a)并不是回到空状态,而是进入一个错误状态,因为此时本单词输入还没有结束,并且永远不可能等于begin了。
状态转移描写的重点在最后几个状态,我把begin检测状态机的代码放出:
1 |
|
这是在检测到“begi”和“begin”时的情况,有以下几点需要注意:
首先,观察波形可以发现,当时钟上升沿输入d时,状态机立刻读取了begin,所以是否读取begin要在“begi且输入n”,也即上面的第一个case中就要判断。
其次,关注到给出波形在endc输入时的波动可以发现,当输入是end时,即使下一个输入不是空格,但在这之间(d输入到c输入间)依然将end看作已经输入,此时改变reg_state值,直到判断下一个输入不是空格,就再把这个输入删除。因此判断输入要在第一个case(reg_state加)而判断是否真的输入了要在第二个case中(如果输入不是空格,证明没有真正输入,reg_state再减回去)
最后,我这里reg_state的基准是1,也即begin和end匹配时其值为1(初始化也为1),因为如果初始化为0时,相当于可能检测到-1,而此时可能就需要符号转换$signed()
,就比较容易出错,所以本题我设定基准是1,这样最少也是0就不会出错(为什么没有负数请看接下来的分析)
最后就是end检测和输出的时候的一点设计:
end:
1 |
|
输出:assign result = (unable == 0 && reg_state == 1) ? 1 : 0;
这其实是为了让reg_state不会为负数的一点设计,但是也挺符合实际的,也就是,当之前字符串已经匹配(即reg_state==1)时出现了end在begin之前的情况,那么我们可以断言在reset之前这个字符串已经不可能符合条件了,因为无法用后置begin抵消前置end,因此只要发现reg_state为0时,就可以让一个寄存器(此处我命名为unable)为1,这时输出一定为0。
综上,当unable为0,而且reg_state为1时是全匹配状态,此时输出result为1。
这就是P1全部作业了……P0第二次上机真得加油,希望自己真的能顺利过关吧!