verilog 手撕代码记录

1.简易式同步fifo,3个always块搞定

`timescale 1ns/1ns  
module sfifo#(
  parameter  WIDTH = 8,
  parameter   DEPTH = 16
)(
  input           clk    , 
  input           rst_n  ,
  input           winc  ,
  input            rinc  ,
  input     [WIDTH-1:0]  wdata  ,
 
  output reg        wfull  ,
  output reg        rempty  ,
  output wire [WIDTH-1:0]  rdata
);
  // 用localparam定义一个参数,可以在文件内使用
    localparam ADDR_WIDTH = $clog2(DEPTH);
 
    reg [ADDR_WIDTH:0] waddr;
    reg [ADDR_WIDTH:0] raddr;
    always @ (posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            waddr <= 'b0;
        end 
        else begin
            if( winc && ~wfull ) begin
                waddr <= waddr + 1'b1;
            end 
            else begin
                waddr <= waddr;    
            end 
        end 
    end 
 
    always @ (posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            raddr <= 'b0;
        end 
        else begin
            if( rinc && ~rempty ) begin
                raddr <= raddr + 1'b1;
            end 
            else begin
                raddr <= raddr;    
            end 
        end 
    end 
 
    always @ (posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            wfull <= 'b0;
            rempty <= 'b0;
        end 
        else begin
            wfull <= (raddr == {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH-1:0]});
            rempty <= (raddr == waddr);
        end 
    end 
 
// 带有 parameter 参数的例化格式    
dual_port_RAM  
    #(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    )
    dual_port_RAM_U0 
    (
        .wclk(clk),
      .wenc(winc),
        .waddr(waddr[ADDR_WIDTH-1:0]), 
      .wdata(wdata),        
      .rclk(clk),
        .renc(rinc),
        .raddr(raddr[ADDR_WIDTH-1:0]), 
      .rdata(rdata)     
);       
endmodule
 
/**************RAM 子模块*************/
module dual_port_RAM #(parameter DEPTH = 16,
             parameter WIDTH = 8)(
   input wclk
  ,input wenc
  ,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
  ,input [WIDTH-1:0] wdata        //数据写入
  ,input rclk
  ,input renc
  ,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
  ,output reg [WIDTH-1:0] rdata     //数据输出
);
 
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
 
always @(posedge wclk) begin
  if(wenc)
    RAM_MEM[waddr] <= wdata;
end 
 
always @(posedge rclk) begin
  if(renc)
    rdata <= RAM_MEM[raddr];
end 
 
endmodule

2.异步fifo,Verilog实现,注意空满逻辑判断,格雷码转换公式,深度计算

二进制转格雷码、格雷码转二进制 见下面的图片

verilog 手撕代码记录_第1张图片

空满逻辑判断 

满:在写时钟域进行判断,如果wptr和rptr_cdc的高两位相反,其他位相同,则满

空:在读时钟域进行判断,如果rptr和wptr_cdc完全相同,则空

异步fifo会出现假空假满现象

异步fifo深度计算

如何简单快速地计算FIFO的最小深度?(笔面试常客)_孤独的单刀的博客-CSDN博客_fifo最小深度

`timescale 1ns/1ns
/********RAM***********/
module dual_port_RAM #(parameter DEPTH = 16,
parameter WIDTH = 8)(
input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];

end
endmodule
/********AFIFO*********/

module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr <= 'b0;
end
else begin
if( winc && ~wfull ) begin
waddr <= waddr + 1'b1;
end
else begin
waddr <= waddr;
end
end
end

always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr <= 'b0;
end
else begin
if( rinc && ~rempty ) begin
raddr <= raddr + 1'b1;
end
else begin
raddr <= raddr;
end
end
end
wire [ADDR_WIDTH:0] waddr_gray;
wire [ADDR_WIDTH:0] raddr_gray;
assign waddr_gray = waddr ^ (waddr>>1);
assign raddr_gray = raddr ^ (raddr>>1);
reg [ADDR_WIDTH:0] waddr_gray_reg;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr_gray_reg <= 'd0;
end
else begin
waddr_gray_reg <= waddr_gray;
end
end
reg [ADDR_WIDTH:0] raddr_gray_reg;
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr_gray_reg <= 'd0;
end
else begin
raddr_gray_reg <= raddr_gray;
end
end
reg [ADDR_WIDTH:0] addr_r2w_t;
reg [ADDR_WIDTH:0] addr_r2w;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin

addr_r2w_t <= 'd0;
addr_r2w <= 'd0;
end
else begin
addr_r2w_t <= raddr_gray_reg;
addr_r2w <= addr_r2w_t;
end
end
reg [ADDR_WIDTH:0] addr_w2r_t;
reg [ADDR_WIDTH:0] addr_w2r;
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
addr_w2r_t <= 'd0;
addr_w2r <= 'd0;
end
else begin
addr_w2r_t <= waddr_gray_reg;
addr_w2r <= addr_w2r_t;
end
end
assign wfull = (waddr_gray_reg == {~addr_r2w[ADDR_WIDTH:ADDR_WIDTH-1], addr_r2w[ADDR_WIDTH-2:0]});
assign rempty = (raddr_gray_reg == addr_w2r);

dual_port_RAM
#(
.DEPTH(DEPTH),
.WIDTH(WIDTH)
)
dual_port_RAM_U0
(
.wclk(wclk),
.wenc(winc&&~wfull),
.waddr(waddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
.wdata(wdata), //数据写入
.rclk(rclk),
.renc(rinc&&~rempty),
.raddr(raddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
.rdata(rdata) //数据输出
);
endmodule

 3.N倍时钟倍频器,下面实现了时钟的4倍频

`timescale 1ns/1ns

module Mul_clk_tb();
reg clk;
reg [3:0] clk_tmp;
wire clk1;
//CYCLE 和 NUM 需满足倍数关系
parameter NUM = 4;
parameter CYCLE = 20;

initial begin
    clk = 1;
    forever begin
        #CYCLE clk = ~clk;
    end
end

genvar i;
generate
for(i=0;i

4.分频器

包括奇数分频和偶数分频,还包括其他特殊的特殊的分频以及改变占空比

分频器 偶分频 奇分频 任意分频___Wang____的博客-CSDN博客

5.边沿检测器

包括上升沿检测和下降沿检测,以及两种边沿同时都检测

边沿检测 Verilog___Wang____的博客-CSDN博客_verilog边沿检测

6.序列检测器  可以使用状态机和移位寄存器两种方法

//状态机法
`timescale 1ns/1ns
module sequence_detect(
    input clk,
    input rst_n,
    input a,
    output reg match
);
    parameter ZERO=0, ONE=1, TWO=2, THREE=3, FOUR=4, FIVE=5, SIX=6, SEVEN=7, EIGHT=8;
    reg [3:0] state, nstate;
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            state <= ZERO;
        else
            state <= nstate;
    end
     
    always@(*) begin
        case(state)
            ZERO   : nstate = a? ZERO : ONE;
            ONE    : nstate = a? TWO  : ONE;
            TWO    : nstate = a? THREE: ONE;
            THREE  : nstate = a? FOUR : ONE;
            FOUR   : nstate = a? ZERO : FIVE;
            FIVE   : nstate = a? TWO  : SIX;
            SIX    : nstate = a? TWO  : SEVEN;
            SEVEN  : nstate = a? EIGHT: ONE;
            EIGHT  : nstate = a? THREE: ONE ;
            default: nstate = ZERO;
        endcase
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            match <= 0;
        else
            match <= state==EIGHT;
    end
   
endmodule

//移位寄存器法
module sequence_detect(
    input clk,
    input rst_n,
    input a,
    output reg match
);
    reg[7:0] a_r;
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) begin
            a_r<='b0;
        end
        else begin
            a_r<={a_r[6:0],a};
        end
    end
   
    always@(posedge clk or negedge rst_n) begin
      if(~rst_n) begin
           match <= 1'b0;
        end
        else begin
           match <= a_r==8'b01110001;
        end
    end
endmodule

7.贩卖机

首先要将问题抽象出来,d1,d2,d3分别是投0.5,1,2三种硬币,out1,out2分别是是否输出饮料和找零多少钱,将d1,d2,d3做成一个数组,减少输入的判断语句,out2两位变量,2‘b0代表找零0,2'b1代表找零0.5,2‘b10代表找零1,2‘b11代表找零1.5元

第二个always块判断的是cur_state,并且对于cur_state的很多状态都直接让next_state为s0;

第三个always块判断的是next_state

`timescale 1ns/1ns
 
 
module seller1(
    input wire clk  ,
    input wire rst  ,
    input wire d1 ,
    input wire d2 ,
    input wire d3 ,
     
    output reg out1,
    output reg [1:0]out2
);
 
    parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 , S4 = 'd4, S5 = 'd5 , S6 = 'd6;
    reg  [2:0]    current_state;
    reg  [2:0]    next_state;
    wire [2:0]   input_state;//将输入组合起来
    assign input_state = {d1,d2,d3};
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end   
     
    always@(*)begin
        case(current_state)
            S0:begin
                case(input_state)
                    3'b100:next_state = S1 ;
                    3'b010:next_state = S2 ;
                    3'b001:next_state = S4 ;
                    default:next_state = next_state;
                endcase
            end
            S1:begin
                case(input_state)
                    3'b100:next_state = S2 ;
                    3'b010:next_state = S3 ;
                    3'b001:next_state = S5 ;
                    default:next_state = next_state; 
                endcase
            end
            S2:begin
                case(input_state)
                    3'b100:next_state = S3 ;
                    3'b010:next_state = S4 ;
                    3'b001:next_state = S6 ;
                    default:next_state = next_state;
                endcase            
            end
             
            default:begin
                next_state = S0;
            end
        endcase
    end
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            out1 <= 1'b0;
            out2 <= 2'b0;
        end
        else begin
            case(next_state)
                    S3:        begin out1 <= 1'b1;out2 <= 2'b0; end 
                    S4:        begin out1 <= 1'b1;out2 <= 2'b1; end 
                    S5:        begin out1 <= 1'b1;out2 <= 2'b10; end 
                    S6:        begin out1 <= 1'b1;out2 <= 2'b11; end 
                    default:   begin out1 <= 1'b0;out2 <= 2'b0; end 
            endcase
        end
    end
  
endmodule

你可能感兴趣的:(IC基础,fpga开发)