文章目录
前言
今天我们做的是第四道题——移位运算与乘法,众所周知,在硬件中进行乘除法运算是比较消耗资源的一种方法,想要在不影响延迟的情况下尽量减少资源消耗,我们必须从硬件的特点上进行设计。接下来便让我们看看如何去解这道题。移位运算与乘法
一、题目描述
已知d为一个8位数,请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效(d给出的信号的上升沿表示写入有效)
信号示意图
波形示意图
输入描述:
输入信号 d, clk, rst
类型 wire
在testbench中,clk为周期5ns的时钟,rst为低电平复位
输出描述:
输出信号 input_grant out
类型 reg
二、实现思路
1.理解移位运算与状态机
1)移位运算
移位运算本身比较好理解,它分为了算数移位和逻辑移位两种,这两者都是非循环的移位操作。若移位赋值目标位数多于源数据,对于有符号数右移,先拿符号位填充多出的 bit 位再按照各自的移位运算方式进行运算。对于无符号数,逻辑移位和算数移位的效果一致,即拿0来补充空缺。
1>算数移位
算数右移(>>>)
当移位数据为有符号数,高位补符号位。
当移位数据为无符号数,高位补0。
算数左移(<<<)
有符号数与无符号数效果一致,空缺拿0来补充。
2>逻辑移位
逻辑右移(>>)
不论移位数据为有符号数、无符号数,高位补零。
逻辑左移(<<)
有符号数与无符号数效果一致,空缺拿0来补充。
2)状态机
1>什么是状态机
状态机的全称是有限状态机(Finite-State Machine,FSM),是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态机不仅是一种电路的描述工具,而且也是一种思想方法,在电路设计的系统级和 RTL 级有着广泛的应用。
2>状态机的类型
Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。状态机可分为 2 类:Moore 状态机和 Mealy 状态机。
Moore 型状态机
Moore 型状态机的输出只与当前状态有关,与当前输入无关。
输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能反映出来。这也是 Moore 型状态机的一个重要特点:输入与输出是隔离开来的。
Mealy 型状态机
Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。
Mealy 型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy 型状态机输出对输入的响应会比 Moore 型状态机早一个时钟周期。
3>状态机的设计流程
根据设计需求画出状态转移图,确定使用状态机类型,并标注出各种输入输出信号,更有助于编程。一般使用最多的是 Mealy 型 3 段式状态机。设计如下:
(0) 首先,根据状态机的个数确定状态机编码。利用编码给状态寄存器赋值,代码可读性更好。
(1) 状态机第一段,时序逻辑,非阻塞赋值,传递寄存器的状态。
(2) 状态机第二段,组合逻辑,阻塞赋值,根据当前状态和当前输入,确定下一个状态机的状态。
(3) 状态机第三段,时序逻辑,非阻塞赋值,因为是 Mealy 型状态机,根据当前状态和当前输入,确定输出信号。
2.具体实现思路
在这道题中输出一共有两个,一个是位宽为11的 out 以及 input_grant ,对于 input_grant 来说比较好判断,只有 out 等于输入时 input_grant 才为1,其余时候都为0。而我们要输出 out 的话需要用到用到移位运算符,当我们数乘1时不用多想直接赋值即可;当我们要数乘3时,相等于将输入 d 左移2位然后减去d;当我们要数乘7时,相等于将输入 d 左移3位然后减去d;当我们要数乘8时,相等于将输入 d 左移3位。总结如下:(din为中间变量)
数乘 | 位运算 |
---|---|
1 | d |
3 | (din<<2)-din |
7 | (din<<3)-din |
8 | (din<<3) |
四种数乘的结果我们已经计算出来了,接下来我们需要使用状态机来将四种状态输出。因此,我们需要定义一个 reg 型变量 cnt 用于计数,从而输出我们的四种状态。
三、代码展示
`timescale 1ns/1ns
module multi_sel(
input [7:0]d ,
input clk,
input rst,
output reg input_grant,
output reg [10:0]out
);
reg [1:0]cnt;//状态机计数变量,用于实现不同状态的输出
reg [7:0]din;//中间变量,用于存储输入d的值
always@(posedge clk or negedge rst) begin
if(!rst) begin //在rst不为1时,不进行运算,所有变量归零
cnt <= 0;
out <= 0;
input_grant <= 0;
din <= 0;
end
else begin //rst为1时开始进行运算
cnt <= cnt+1; //用于累计次数,当累计到3时清零
case (cnt)
0: begin //数乘1
din <= d;
input_grant <= 1;
out <= d;
end
1: begin //数乘3
input_grant <= 0;
out <= (din<<2)-din;
end
2: begin //数乘7
input_grant <= 0;
out <= (din<<3)-din;
end
3: begin //数乘8
input_grant <= 0;
out <= (din<<3);
end
endcase
end
end
endmodule
总结
以上就是我在做这道题时的思路,以及代码的编写,如果还有更多更好的解法,欢迎读到这篇文章的朋友们在评论区告诉我,共同进步嘛。