FPGA(六)RTL代码之二(复杂电路设计1)

系列文章目录

FPGA(五)RTL代码之一(跨时钟域设计)


前言

这不马上要面试了嘛,有些慌!HDLbits的题目已经刷完了,但又知道自己还远远不够,就从一个B站UP主那里截取了一个刷题的题库,只有题目,代码都要自己去写,所以写的代码可能存在问题,我打算分成好几节把刷的题目代码放出来。仅供自己学习。如果有需要的也可以看看,若有错误,还望指出!谢谢!

请添加图片描述

一、同步FIFO

详细内容请参考我的另一篇博客FPGA(四)数字IC面试的四个基本问题第四章第二节。

二、ALU算术逻辑单元

参考:verilog实现算术逻辑单元ALU
Verilog组合设计:ALU算术逻辑单元

RTL代码

module alu (
    input   [11:0] alu_control,
    input   [31:0] alu_src1,
    input   [31:0] alu_src2,
    output  [31:0] alu_result
); 
    //control
    wire op_add     = alu_control[0];
    wire op_sub     = alu_control[1];
    wire op_slt     = alu_control[2];
    wire op_sltu    = alu_control[3];
    wire op_and     = alu_control[4];
    wire op_nor     = alu_control[5];
    wire op_or      = alu_control[6];
    wire op_xor     = alu_control[7];
    wire op_sll     = alu_control[8];
    wire op_srl     = alu_control[9];
    wire op_sra     = alu_control[10];
    wire op_lui     = alu_control[11];

    //result
    wire [31:0]     add_sub_res;
    wire [31:0]     slt_res;
    wire [31:0]     sltu_res;
    wire [31:0]     and_res;
    wire [31:0]     nor_res;
    wire [31:0]     or_res;
    wire [31:0]     xor_res;
    wire [31:0]     sll_res;
    wire [31:0]     srl_res;
    wire [31:0]     sra_res;
    wire [31:0]     lui_res;

    //logic
    assign  and_res = alu_src1 & alu_src2;
    assign  or_res  = alu_src1 | alu_src2;
    assign  xor_res = alu_src1 ^ alu_src2;
    assign  nor_res = ~or_res;
    assign  lui_res = {alu_src2[15:0], 16'b0};

    //adder
    wire    [31:0] adder1;
    wire    [31:0] adder2;
    wire           adder_cin;
    wire    [31:0] adder_res;
    wire           adder_cout;
    assign  adder1      = alu_src1;
    assign  adder2      = (op_sub | op_slt | op_sltu) ? ~alu_src2 : alu_src2;
    assign  adder_cin   = (op_sub | op_slt | op_sltu) ? 1'b1 : 1'b0;
    assign  {adder_cout,adder_res} = adder1 + adder2 + adder_cin;

    assign  add_sun_res = adder_res;

    //slt && sltu
    assign slt_res [31:1] = 31'b0;
    assign sltu_res[31:1] = 31'b0;
    assign sltu_res[0]    = ~adder_cout;
    assign slt_res [0]    = (alu_src1[31] & ~alu_src2[31])
                          | ( ~(alu_src1[31] ^ alu_src2[31]) & adder_res[31]) ;
    
    //sll && srl && sra
    assign sll_res = alu_src2 << alu_src1[4:0];
    assign srl_res = alu_src2 >> alu_src1[4:0];
    assign sra_res = ($signed(alu_src2)) >>> alu_src1[4:0];

    //cout_result
    assign alu_result = ({32{op_add | op_sub}} & add_sub_res)
                      | ({32{op_slt         }} & slt_res)
                      | ({32{op_sltu        }} & sltu_res)
                      | ({32{op_and         }} & and_res)
                      | ({32{op_nor         }} & nor_res)
                      | ({32{op_or          }} & or_res)
                      | ({32{op_xor         }} & xor_res)
                      | ({32{op_sll         }} & sll_res)
                      | ({32{op_srl         }} & srl_res)
                      | ({32{op_sra         }} & sra_res)
                      | ({32{op_lui         }} & lui_res);
endmodule

三、二进制转格雷码

详细解释请参考:Verilog实现二进制码与格雷码的转换

二进制格雷码
00000000
00010001
00100011
00110010
01000110
01010111
01100101
01110100
10001100
10011101
10101111
10111110
11001010
11011011
11101001
11111000

RTL代码

module test #(
    parameter   WIDTH = 8
)(
    input [WIDTH-1:0]   Binary,
    output[WIDTH-1:0]   Gray
);
reg [WIDTH-1:0] BGray;
always @(*) begin
    BGray = (Binary >> 1) ^ Binary;
end
assign Gray = BGray;

endmodule

四、格雷码转二进制

原理: 若二进制格雷码表示为:
G [ N − 1 ] G [ N − 2 ] … G [ 2 ] G [ 1 ] G [ 0 ] ; G[N-1]G[N-2]…G[2]G[1]G[0]; G[N1]G[N2]G[2]G[1]G[0];
相应地, 则二进制码表示为:
B [ N − 1 ] B [ N − 2 ] … B [ 2 ] B [ 1 ] B [ 0 ] . B[N-1]B[N-2]…B[2]B[1]B[0]. B[N1]B[N2]B[2]B[1]B[0].
其中最高位保留: B [ N − 1 ] = G [ N − 1 ] B[N-1] = G[N-1] B[N1]=G[N1];其他各位: B [ i − 1 ] = G [ i − 1 ] x o r B [ i ] . ( i = 1 , 2 , … , n − 1 ) B[i-1] = G[i-1] xor B[i]. (i = 1, 2, …, n-1) B[i1]=G[i1]xorB[i].(i=1,2,,n1)

module test #(
	parameter WIDTH = 8
) (
	input 	[WIDTH-1:0] Gray, 
	output 	[WIDTH-1:0] Bin_out
); 
reg [WIDTH-1:0] Binary; 
integer i;
always @ (*)begin    
    Binary[WIDTH-1] = Gray[WIDTH-1];    
    for(i = (WIDTH-2); i >= 0; i = i-1)     
        Binary[i] = Binary[i+1] ^ Gray[i];
end
assign Bin_out = Binary;
endmodule

五、二进制转BCD码-Double dabble

详细参考维基百科Double dabble
双涉猎算法用于将二进制数转换为二进制编码的十进制(BCD)表示法。它也被称为shift-and-add-3算法,可以使用计算机硬件中的少量门来实现,但以高延迟为代价。

算法操作如下:

  1. 假设要转换的原始数字存储在 n 位宽的寄存器中。保留一个足够宽的暂存空间,以容纳原始数字及其BCD表示形式; n + 4 × c e i l ( n / 3 ) n + 4×ceil(n/3) n+4×ceiln/3位就足够了。最多需要 4 位二进制文件来存储每个十进制数字。
  2. 然后将暂存空间划分为 BCD 数字(左侧)和原始寄存器(右侧)。例如,如果要转换的原始数字为八位宽,则暂存空间将按如下方式分区:
    在这里插入图片描述
    上图显示了原始寄存器中 24 3 10 243_{10} 24310 的二进制表示形式,左侧显示了 243 的 BCD 表示形式。
    暂存空间初始化为全部零,然后要转换的值被复制到右侧的“原始寄存器”空间中。
  3. 然后,该算法迭代 n 次。在每次迭代中,任何至少为5(二进制为0101)的BCD数字都会递增3(0011);然后整个划痕空间左移一点。增量可确保值 5(递增和左移)变为 16 (10000),从而正确地“携带”到下一个 BCD 数字中。
    从本质上讲,该算法的工作原理是每次迭代时将左侧的 BCD 值加倍,并根据原始位模式添加一个或零个。左移可同时完成这两项任务。如果任何数字为 5 或更高,则添加 3 以确保以 10 为基数的值“携带”。

对值 24 3 10 243_{10} 24310 执行的双涉猎算法如下所示:
在这里插入图片描述
RTL代码

// parametric Verilog implementation of the double dabble binary to BCD converter
// for the complete project, see
// https://github.com/AmeerAbdelhadi/Binary-to-BCD-Converter
module bin2bcd
 #( parameter                W = 18)  // input width
  ( input      [W-1      :0] bin   ,  // binary
    output reg [(W+(W-4))/3:0] bcd   ); // bcd {...,thousands,hundreds,tens,ones}

  integer i,j;

  always @(bin) begin
    for(i = 0; i <= W+(W-4)/3; i = i+1) 
    	bcd[i] = 0;     // initialize with zeros
    bcd[W-1:0] = bin;                                   // initialize with input vector
    for(i = 0; i <= W-4; i = i+1)                       // iterate on structure depth
      for(j = 0; j <= i/3; j = j+1)                     // iterate on structure width
        if (bcd[W-i+4*j -: 4] > 4)                      // if > 4
          bcd[W-i+4*j -: 4] = bcd[W-i+4*j -: 4] + 4'd3; // add 3
  end

endmodule

六、二进制转BCD码-基于Double dabble的有限状态机

可实现位宽可变。
第一来源为Convert Binary numbers to BCD in VHDL and Verilog请添加图片描述
RTL代码

///
// File Downloaded from http://www.nandland.com
///
module Binary_to_BCD
  #(parameter INPUT_WIDTH,
    parameter DECIMAL_DIGITS)
  (
   input                         i_Clock,
   input [INPUT_WIDTH-1:0]       i_Binary,
   input                         i_Start,
   //
   output [DECIMAL_DIGITS*4-1:0] o_BCD,
   output                        o_DV
   );
   
  parameter s_IDLE              = 3'b000;
  parameter s_SHIFT             = 3'b001;
  parameter s_CHECK_SHIFT_INDEX = 3'b010;
  parameter s_ADD               = 3'b011;
  parameter s_CHECK_DIGIT_INDEX = 3'b100;
  parameter s_BCD_DONE          = 3'b101;
   
  reg [2:0] r_SM_Main = s_IDLE;
   
  // The vector that contains the output BCD
  reg [DECIMAL_DIGITS*4-1:0] r_BCD = 0;
    
  // The vector that contains the input binary value being shifted.
  reg [INPUT_WIDTH-1:0]      r_Binary = 0;
      
  // Keeps track of which Decimal Digit we are indexing
  reg [DECIMAL_DIGITS-1:0]   r_Digit_Index = 0;
    
  // Keeps track of which loop iteration we are on.
  // Number of loops performed = INPUT_WIDTH
  reg [7:0]                  r_Loop_Count = 0;
 
  wire [3:0]                 w_BCD_Digit;
  reg                        r_DV = 1'b0;                       
    
  always @(posedge i_Clock)
    begin
 
      case (r_SM_Main) 
  
        // Stay in this state until i_Start comes along
        s_IDLE :
          begin
            r_DV <= 1'b0;
             
            if (i_Start == 1'b1)
              begin
                r_Binary  <= i_Binary;
                r_SM_Main <= s_SHIFT;
                r_BCD     <= 0;
              end
            else
              r_SM_Main <= s_IDLE;
          end
                 
  
        // Always shift the BCD Vector until we have shifted all bits through
        // Shift the most significant bit of r_Binary into r_BCD lowest bit.
        s_SHIFT :
          begin
            r_BCD     <= r_BCD << 1;
            r_BCD[0]  <= r_Binary[INPUT_WIDTH-1];
            r_Binary  <= r_Binary << 1;
            r_SM_Main <= s_CHECK_SHIFT_INDEX;
          end          
         
  
        // Check if we are done with shifting in r_Binary vector
        s_CHECK_SHIFT_INDEX :
          begin
            if (r_Loop_Count == INPUT_WIDTH-1)
              begin
                r_Loop_Count <= 0;
                r_SM_Main    <= s_BCD_DONE;
              end
            else
              begin
                r_Loop_Count <= r_Loop_Count + 1;
                r_SM_Main    <= s_ADD;
              end
          end
                 
  
        // Break down each BCD Digit individually.  Check them one-by-one to
        // see if they are greater than 4.  If they are, increment by 3.
        // Put the result back into r_BCD Vector.  
        s_ADD :
          begin
            if (w_BCD_Digit > 4)
              begin                                     
                r_BCD[(r_Digit_Index*4)+:4] <= w_BCD_Digit + 3;  
              end
             
            r_SM_Main <= s_CHECK_DIGIT_INDEX; 
          end       
         
         
        // Check if we are done incrementing all of the BCD Digits
        s_CHECK_DIGIT_INDEX :
          begin
            if (r_Digit_Index == DECIMAL_DIGITS-1)
              begin
                r_Digit_Index <= 0;
                r_SM_Main     <= s_SHIFT;
              end
            else
              begin
                r_Digit_Index <= r_Digit_Index + 1;
                r_SM_Main     <= s_ADD;
              end
          end
         
  
  
        s_BCD_DONE :
          begin
            r_DV      <= 1'b1;
            r_SM_Main <= s_IDLE;
          end
         
         
        default :
          r_SM_Main <= s_IDLE;
            
      endcase
    end // always @ (posedge i_Clock)  
 
   
  assign w_BCD_Digit = r_BCD[r_Digit_Index*4 +: 4];
       
  assign o_BCD = r_BCD;
  assign o_DV  = r_DV;
      
endmodule // Binary_to_BCD

仿真文件参考:FPGA Verilog实现二进制转BCD码

`timescale 1ns/1ps

module Binary_to_BCD_tb;

reg clk;
reg rst_n;

reg start;
reg [7:0] bin;
wire [11:0] dec;
wire done;
wire [3:0] dec_b = dec[11:8];
wire [3:0] dec_s = dec[7:4];
wire [3:0] dec_g = dec[3:0];
wire [7:0] dec_real = dec_b * 100 + dec_s * 10 + dec_g;

reg [7:0] idx;
always # (10/2) clk <= !clk;

initial begin
    clk = 0;
    rst_n = 0;
    start = 0;
    bin = 0;
    idx = 0;
    #50
    @(posedge clk);
    rst_n = 1;
    #50
    @(posedge clk);
    
    for(idx = 0; idx < 254; idx = idx + 1)
        trig_bcd_covert(idx);

    #2000;
    $stop();
    $display("stop");
end

task trig_bcd_covert(
    input [7:0] i_bin
);  
begin
    #80
    @(posedge clk);
    bin = i_bin;
    start = 1;
    @(posedge clk);
    start = 0;
    @(posedge done);
    if(dec_real == i_bin)
        $display("ok:%3d -> %1x%1x%1x", bin, dec_b, dec_s, dec_g);
    else 
        $display("err:%3d -> %1x%1x%1x", bin, dec_b, dec_s, dec_g);
end
endtask

Binary_to_BCD #(
    .INPUT_WIDTH(8),
    .DECIMAL_DIGITS(3)
)Binary_to_BCD_ut0(
    //Inputs
    .i_Clock(clk),
    .i_Rst_n(rst_n),
    .i_Binary(bin[7:0]),
    .i_Start(start),
    
    //Outputs
    .o_BCD(dec[11:0]),
    .o_DV(done)
);

endmodule

另外,基于查找表的方法可以参考:FPGA Verilog实现二进制转BCD码2

七、自动售货机

直接参考这个教学文章:6.3 Verilog 状态机

八、秒计数器设计

秒计数器,就是根据时钟频率数数,时钟频率有多大就数多少个数。比如时钟频率为Fre Hz,那么数到(Fre-1)就是1s了。但因为数字电路中的时序逻辑原因,不能等数到(Fre-1)再去操作,而是应该在(Fre-2)的时候去“写”操作,这样在(Fre-1)时才能执行操作。但是,要数到(Fre-1)才能去清零,去数下一轮。

RTL代码

module clock
(
     input wire         sys_clk     ,
     input wire         sys_rst_n   ,
     
     output reg [5:0]   cnt_s       ,
     output reg [5:0]   cnt_m
);

reg [13:0]  cnt_1000;
reg    en_1;
/****************************************************
这里是以1kHz举例的,如果是更高的频率,数就会更大
****************************************************/
/*****************<  分频  >****************************/
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1000 <= 12'd0;
    else if(cnt_1000 == 999)
        cnt_1000 <= 12'd0;
    else    
        cnt_1000 <= cnt_1000 +12'd1;
/*****************<  重要标志位  >****************************/
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        en_1 <= 1'b0;
    else if (cnt_1000 == 998)
        en_1 <= 1'b1;
    else
        en_1 <= 1'b0;
/*****************<  秒计数  >****************************/
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_s <= 6'd0;
    else if ((cnt_s == 6'd59) && (en_1 == 1'b1))
        cnt_s <= 6'd0;
    else if(en_1 == 1'b1)
        cnt_s <= cnt_s + 6'd1;
    else
        cnt_s <= cnt_s;
/*****************<  分计数  >****************************/
always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt_m <= 1'b0; 
    else if ((cnt_s == 6'd59) && (en_1 == 1'b1))
        cnt_m <= cnt_m + 1'b1;
    else
        cnt_m <= cnt_m;    
endmodule


总结

代码为主!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值