• 11.7加减计数器,可置位~,数字钟分秒,串转并,串累加转并,24位串并128,流水乘法器,一些乘法器


     信号发生器

    方波,就是一段时间内都输出相同的信号

     

    锯齿波就是递增

    三角波就是先增后减

    加减计数器

    当mode为1则加,Mode为0则减;只要为0就输出zero 

    这样会出问题,因为要求是十进制,但是这里并没有考虑到9之后怎么办,所以就会使number输出超过9,应该额外要添加十进制的边缘判断,即mode为1,要加的时候也要判断一下是不是要继续加,而不是直接加

    简易秒表

    输出端口second为1~60,到60时,minute+1,分到60时,停止计数

    秒的确定

    分的确定

    可置位计数器

    就是有置位信号时,把当前数字置为要置的数字

    然后要确定是十六进制

    额外逻辑

    串转并

    输入端输入单位信号,累积到6个后,输出一个六位的信号

    1. reg [5:0] data_reg;
    2. reg [2:0] data_cnt;
    3. always @(posedge clk or negedge rst_n ) begin
    4. if(!rst_n)
    5. ready_a <= 'd0;
    6. else
    7. ready_a <= 1'd1;
    8. end
    9. always @(posedge clk or negedge rst_n ) begin
    10. if(!rst_n)
    11. data_cnt <= 'd0;
    12. else if(valid_a && ready_a)
    13. data_cnt <= (data_cnt == 3'd5) ? 'd0 : (data_cnt + 1'd1);
    14. end
    15. always @(posedge clk or negedge rst_n ) begin
    16. if(!rst_n)
    17. data_reg <= 'd0;
    18. else if(valid_a && ready_a)
    19. data_reg <= {data_a, data_reg[5:1]};
    20. end
    21. always @(posedge clk or negedge rst_n ) begin
    22. if(!rst_n)begin
    23. valid_b <= 'd0;
    24. data_b <= 'd0;
    25. end
    26. else if(data_cnt == 3'd5)begin
    27. valid_b <= 1'd1;
    28. data_b <= {data_a, data_reg[5:1]};
    29. end
    30. else
    31. valid_b <= 'd0;
    32. end

    数据累加输出

    当接受到4个输入数据后,输出一个这四个数据的累加结果

    1. `timescale 1ns/1ns
    2. module valid_ready(
    3. input clk ,
    4. input rst_n ,
    5. input [7:0] data_in ,
    6. input valid_a ,
    7. input ready_b ,
    8. output ready_a ,
    9. output reg valid_b ,
    10. output reg [9:0] data_out
    11. );
    12. reg [1:0] data_cnt;
    13. assign ready_a = !valid_b | ready_b;
    14. always @(posedge clk or negedge rst_n ) begin
    15. if(!rst_n)
    16. data_cnt <= 'd0;
    17. else if(valid_a && ready_a)
    18. data_cnt <= (data_cnt == 2'd3) ? 'd0 : (data_cnt + 1'd1);
    19. end
    20. always @(posedge clk or negedge rst_n ) begin
    21. if(!rst_n)
    22. valid_b <= 'd0;
    23. else if(data_cnt == 2'd3 && valid_a && ready_a)
    24. valid_b <= 1'd1;
    25. else if(valid_b && ready_b)
    26. valid_b <= 1'd0;
    27. end
    28. always @(posedge clk or negedge rst_n ) begin
    29. if(!rst_n)
    30. data_out <= 'd0;
    31. else if(ready_b && valid_a && ready_a && (data_cnt == 2'd0))
    32. data_out <= data_in;
    33. else if(valid_a && ready_a)
    34. data_out <= data_out + data_in;
    35. end
    36. endmodule

    非阻塞赋值,就是把次态给现态,就是说右侧的是次态,但是现在还不用,是下个状态的情况,那么条件判断里就是导致其进入下个状态的条件

    非整数倍数据位宽转换24to128

    数据位宽转换,24位转128位,先到的数据为高位

    也是串转并的一种,只不过最后的时候是只要一部分

    思路都是,先判断输入的有效性,有效时,就对数据暂存器做出改变;对于输出时,如果可以输出了,即让输出,就输出,没有使能,就不输出,依然暂存。

    24和128的最小公倍数为384,所以每到384的时候,就是对齐了一次,即完成一次周期

    所以每当cnt到5,10,15时,就需要输出一次,并拉高valid_out一个周期

    简单的输入信号计数器,表示已经输入了几个24位的信号

    数据暂存器,每当输入有效时,将数据从低位移入,注意是低位,而且是要在输入有效时操作

    输出使能

    需要注意的是,非阻塞是使的关系,即这个clk里收到了信号,但是并不在这个周期里即时发生改变,而是在下个clk里再发生改变,也就使得逻辑上同步的输入输出,不是在同一个周期上发生,而是先有输入,才有下个周期对应的输出,每个周期输出的都是上个周期的结果。当下输入的数据,在下个周期出结果

    但在赋值时,由于是非阻塞,所以也是在给次态赋值,所以这个周期里干的事,都不是这个周期里的,而是去确定下个周期的,这个周期里的事都在上个周期里确定了;也就是说此时的条件都是下个周期的,而不是当下的。

    所以写的时候,就不要想的是串行。而是想写的是一个一个模块,根据不同的输入给出不同的输出

    就是注意

     先到的数据在高位,满的时候,先从高位出去,即FIFO。只是截取高位的时候,是从暂存器的低位开始截取的,也就是说,还是先进的最高位先出去;然后输出的时候,是先尝试输出再寄存,因为寄存的时候不管截不截取,它就是全部存进去。但是输出的时候,要判断并截取一部分新的

    valid_in是判断当前的输出是不是有效,如果无效,即使输入了,要发生一些改变时,也不会

     

    之前担心的是,如果能输出的时候,先在暂存器里输入了一遍,结果又在输入里输入了一遍

    但实际上,这就是为什么要用非阻塞而不是阻塞。即各个模块都是并行的,都是并行的,即在这个时钟刻里,用的都是上个时间里的数值,而且不会发生改变。用非阻塞可以不用考虑这样的先后问题,如果是阻塞,就必须先尝试输出,才能暂存

    用阻塞也会出问题,出时序问题,马丹

    1. `timescale 1ns/1ns
    2. module width_24to128(
    3. input clk ,
    4. input rst_n ,
    5. input valid_in ,
    6. input [23:0] data_in ,
    7. output reg valid_out ,
    8. output reg [127:0] data_out
    9. );
    10. reg [3:0] cnt;
    11. reg [127:0] data_lock;
    12. always@(posedge clk or negedge rst_n) begin
    13. if(~rst_n)
    14. cnt <= 0;
    15. else
    16. cnt <= ~valid_in? cnt:cnt+1;
    17. end
    18. always@(posedge clk or negedge rst_n) begin
    19. if(~rst_n)
    20. valid_out <= 0;
    21. else
    22. valid_out <= (cnt==5 || cnt==10 || cnt==15)&&valid_in;
    23. end
    24. always@(posedge clk or negedge rst_n) begin
    25. if(~rst_n)
    26. data_lock <= 0;
    27. else
    28. data_lock <= valid_in? {data_lock[103:0], data_in}: data_lock;
    29. end
    30. always@(posedge clk or negedge rst_n) begin
    31. if(~rst_n)
    32. data_out <= 0;
    33. else if(cnt==5)
    34. data_out <= valid_in? {data_lock[119:0], data_in[23:16]}: data_out;
    35. else if(cnt==10)
    36. data_out <= valid_in? {data_lock[111:0], data_in[23: 8]}: data_out;
    37. else if(cnt==15)
    38. data_out <= valid_in? {data_lock[103:0], data_in[23: 0]}: data_out;
    39. else
    40. data_out <= data_out;
    41. end
    42. endmodule

    流水线乘法器 

    流水线

    就是采用乘法竖式的思想,将乘法转化加法,最高位为n,则就有n个加法数

    用循环简化代码

    1. `timescale 1ns/1ns
    2. module multi_pipe#(
    3. parameter size = 4
    4. )(
    5. input clk ,
    6. input rst_n ,
    7. input [size-1:0] mul_a ,
    8. input [size-1:0] mul_b ,
    9. output reg [size*2-1:0] mul_out
    10. );
    11. wire [2*size-1 : 0] a,b;
    12. reg [2*size-1 : 0]temp0,temp1,temp2,temp3;
    13. assign a=mul_a;
    14. assign b=mul_b;
    15. always @(posedge clk or negedge rst_n)
    16. begin
    17. if(!rst_n)
    18. begin
    19. temp0<=0;
    20. temp1<=0;
    21. temp2<=0;
    22. temp3<=0;
    23. end
    24. else
    25. begin
    26. temp0 <= b[0] ? a : 0;
    27. temp1<= b[1] ? a<<1 : 0;
    28. temp2<= b[2] ? a<<2 : 0;
    29. temp3<= b[3] ? a<<3 : 0;
    30. end
    31. end
    32. always @ (posedge clk or negedge rst_n)
    33. begin
    34. if(!rst_n)
    35. begin
    36. mul_out=0;
    37. end
    38. else
    39. begin
    40. mul_out=temp0+temp1+temp2+temp3;
    41. end
    42. end
    43. endmodule

     

    1. `timescale 1ns/1ns
    2. module multi_pipe#(
    3. parameter size = 4
    4. )(
    5. input clk ,
    6. input rst_n ,
    7. input [size-1:0] mul_a ,
    8. input [size-1:0] mul_b ,
    9. output reg [size*2-1:0] mul_out
    10. );
    11. //parameter
    12. parameter N = size * 2;
    13. //defination
    14. wire [N - 1 : 0] temp [0 : 3];
    15. reg [N - 1 : 0] adder_0;
    16. reg [N - 1 : 0] adder_1;
    17. //output
    18. genvar i;
    19. generate
    20. for(i = 0; i < 4; i = i + 1)begin : loop
    21. assign temp[i] = mul_b[i] ? mul_a << i : 'd0;
    22. end
    23. endgenerate
    24. always@(posedge clk or negedge rst_n)begin
    25. if(!rst_n) adder_0 <= 'd0;
    26. else adder_0 <= temp[0] + temp[1];
    27. end
    28. always@(posedge clk or negedge rst_n)begin
    29. if(!rst_n) adder_1 <= 'd0;
    30. else adder_1 <= temp[2] + temp[3];
    31. end
    32. always@(posedge clk or negedge rst_n)begin
    33. if(!rst_n) mul_out <= 'd0;
    34. else mul_out <= adder_0 + adder_1;
    35. end
    36. endmodule

     

    就是说,一个数位数是否为1,决定另一个数是否为拓位后的数还是0

    状态图实现任意位乘法

    2个32位整数相乘,实际上是进行了32次加法操作

    流程图中,x因为需要左移,所以32位长度的x应该用一个64位寄存器来存储,这样才能保证x左移后不会发生高位丧失。

    取绝对值操作

    首先是获取符号位,然后根据符号位去决定对应的操作

    如果是正数,就直接赋值;不然,就先取反再+1

    输入为mult_begin,拉高后乘法再开始,直到运算结束,或者人为拉低

    需要注意的是,右移y,那么每次都是去掉y的最低位,然后需要在最高位补0,即整体往右移动一位;这个块就是实现每次都右移一位y

    这个是左移x,不会丢位,因为最多移32次,最多就是到最高位

    然后这个是判断加数,如果此时y的最低位是1,那么就加;不然,就不加为0;

    符号位确定

    循环实现

    相同的思路,第二种就是用for循环简化了代码

    采用移位寄存器同样可以实现,上面那个是每次都计算,都是从头开始移位i次,采用移位寄存器后就是不断复用上一次的结果,只移位一次就可以,而不是每次都移位i次

    用i,i就代表移位的次数,可以简便的读取到第i位,以及左移i位的结果

    流水线是每次都加两个新的,

    仿真文件

    1. `timescale 1ns / 1ps
    2. module tb;
    3. // Inputs
    4. reg clk;
    5. reg mult_begin;
    6. reg [31:0] mult_op1;
    7. reg [31:0] mult_op2;
    8. // Outputs
    9. wire [63:0] product;
    10. wire mult_end;
    11. // Instantiate the Unit Under Test (UUT)
    12. multiply uut (
    13. .clk(clk),
    14. .mult_begin(mult_begin),
    15. .mult_op1(mult_op1),
    16. .mult_op2(mult_op2),
    17. .product(product),
    18. .mult_end(mult_end)
    19. );
    20. initial begin
    21. // Initialize Inputs
    22. clk = 0;
    23. mult_begin = 0;
    24. mult_op1 = 0;
    25. mult_op2 = 0;
    26. // Wait 100 ns for global reset to finish
    27. #100;
    28. mult_begin = 1;
    29. mult_op1 = 32'H00001111;
    30. mult_op2 = 32'H00001111;
    31. #400;
    32. mult_begin = 0;
    33. #500;
    34. mult_begin = 1;
    35. mult_op1 = 32'H00001111;
    36. mult_op2 = 32'H00002222;
    37. #400;
    38. mult_begin = 0;
    39. #500;
    40. mult_begin = 1;
    41. mult_op1 = 32'H00000002;
    42. mult_op2 = 32'HFFFFFFFF;
    43. #400;
    44. mult_begin = 0;
    45. #500;
    46. mult_begin = 1;
    47. mult_op1 = 32'H00000002;
    48. mult_op2 = 32'H80000000;
    49. #400;
    50. mult_begin = 0;
    51. // Add stimulus here
    52. end
    53. always #5 clk = ~clk;
    54. endmodule

    一些细节 

    这是两者的位数关系

    非流水线设计就是每次乘法运算只输出一个结果

  • 相关阅读:
    QEMU(Quick EMUlator)学习
    Jmeter和Postman那个工具更适合做接口测试?
    Tornado服务实现文件下载功能
    知识图谱+推荐系统 文献阅读
    js foreach与for循环之return跳出循环
    运行open62541基于TSN网络的pub/sub示例(ETH+ UDP)
    CASAIM与南京航空航天大学在自动化叶片曲面分析系统开展合作,推动航空航天发动机零部件自动化3D检测进程
    山西电力市场日前价格预测【2023-11-11】
    为什么开源大模型终将胜出?
    【Java】LambdaStream
  • 原文地址:https://blog.csdn.net/m0_73553411/article/details/134262550