题目要求:
已知d为一个8位数,请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效(d给出的信号的上升沿表示写入有效)
知识储备:
如何实现对一个8位数d乘以1/3/7/8倍数。
采用移位和拼接的方式来实现。
移位
移位都默认补0,左移低位补0,表示乘法,右移高位补0,表示除法。
位拼接
例:a = 2’b10 ; b = 3’b011 ; 那么:
c = {a,b} = 5’b10011
d = {2’b00,b} = 5’b00011
复制+拼接
e = {{2{a}},b} =7’b1010 011
采用移位运算符号实现乘除法:
a = b<<1,左移一位,表示乘以2
a = b>>1,右移一位,表示除以2
- 如何遇到奇数的乘除法怎么办?
进行拆分,比如说乘以3,即可拆分成 a + a2 ;
比如说乘以7,即可拆分成 a + a2 + a*4 ;
至此,即可来看所给的题目。
信号示意图:
input_grant 表示输入数据d有效
波形示意图:
通过波形可看到,输入数据d分别为 143 ,7 ,6 ,128 ,129
但是对应看输出out发现,四种情况的乘法运算 忽略了 d =6 ,因为d = 6 的时候,上一个输入数据d = 7 还没有计算完成,同时d = 6 的时候,对应的input_grant信号为低电平,说明d = 6 数据的输入是无效的,从而没有将其进行四种情况的乘法计算。
由于我们需要对输入数据进行四种乘法运算,所以需要用到移位拼接操作,另外四种情况可以通过case语句来实现,case(控制表达式),其中控制表达式可采用计数器,设计计数器来表示四种情况,第一次计算,第二次计算,第三次第四次计算。
当我在进行计数器设计的时候,我的想法是不仅要设计计数器,还要让它满四次清零,否则会一直计数,后来发现这种想法和操作多此一举,因为定义的计数器是2位的,所以最多只能计数四次,0,1,2,3,当四次计数完成后,也会继续0开始。
关于case语句里面写什么?
- 每次情况下的输出是多少(也就是该输出是乘以1/3/7/8中的哪一个)。
- 引入变量d_reg来寄存d的数据,防止进行多次不同乘法的时候d数值改变。
- input_grant 作为有效信号,要搞清楚其输出的值什么时候是,什么时候是0,因为一个数据计算四次,然后对应接收的下一个数据才是有效的,才可进行下一次计算,因此每第一次乘法运算的时候,input_grant= 1,其余情况为0,所以需要将此信号进行相应的输出。
如下是Verilog代码:
`timescale 1ns/1ns
module multi_sel(
input [7:0]d ,
input clk,
input rst,
output reg input_grant,
output reg [10:0]out
);
//*************code***********//
reg [1:0] cnt;
//设计一个计数器,数据有效则开始计数
always @ (posedge clk or negedge rst)begin
if(!rst)
cnt <= 2'b0;
else
cnt <= cnt + 2'b1;
end
reg [10:0] d_reg;
always @ (posedge clk or negedge rst)begin
if(!rst)begin
out <= 11'b0;
input_grant <= 1'b0;
d_reg <= 8'b0;
end
else begin
case(cnt)
2'd0: begin
out <= d; //乘以1倍数
d_reg <= d;
input_grant <= 1'b1;
end
2'd1: begin
out <= d_reg + {d_reg,1'b0}; //乘以3倍数
//out <= d_reg + (d_reg<<1); //偶数倍乘法,可用拼接,也可用移位
input_grant <= 1'b0;
end
2'd2: begin
out <= d_reg + {d_reg,1'b0} + {d_reg,2'b00} ; //乘以7倍数
input_grant <= 1'b0;
end
2'd3: begin
//out <= {d_reg,3'b000} ; //乘以8倍数
out <= d_reg << 3 ;
input_grant <= 1'b0;
end
default:out <= d_reg;
endcase
end
end
//*************code***********//
endmodule