牛客 Verilog 刷题入门篇1~24 + 进阶篇1~34 题解代码,所有代码均能通过测试,配合视频讲解效果更佳。为避免内容冗余,本文只给出代码,部分题目给出必要说明。

很多题目本身出题有些问题,着重理解题目,没必要钻牛角尖。

本文作者:FPGA探索者

目录

文章目录

  • 视频讲解合集
  • 入门篇1~24题
    • VL1 四选一多路器
      • 解法一 三目运算符
      • 解法二 case语句
      • 三目运算符?:
      • case语句
    • VL2 异步复位的串联T触发器
    • VL3 奇偶校验(实际上应该是奇偶检测)
    • VL4 移位运算与乘法
    • VL5 位拆分与运算
    • VL6 多功能数据处理器
    • VL7 求两个数的差值
    • VL8 使用generate...for语句简化代码
      • 使用generate...for
      • 使用for
    • VL9 使用子模块实现三输入数的大小比较
      • 解法一 使用时序逻辑子模块
      • 解法二 使用组合逻辑子模块,需要打两拍
    • VL10 使用函数实现数据大小端转换
    • VL11 4位数值比较器电路
    • VL12 4bit超前进位加法器电路
    • VL13 优先编码器电路1
    • VL14 用优先编码器1实现键盘编码电路
    • VL15 优先编码器2——8线-3线优先编码器
    • VL16 使用8线-3线优先编码器实现16线-4线优先编码器
    • VL17~20 不建议做
    • VL21 根据状态转移表实现时序电路
    • VL22 使用状态转移图实现时序电路
    • VL23 ROM的简单实现
    • VL24 边沿检测
  • 进阶篇1~34题
    • VL1 输入序列连续的序列检测
    • VL2 还有无关项的序列检测
    • VL3 不重叠序列检测
    • VL4 输入序列不连续的序列检测
    • VL5 信号发生器
    • VL6 数据串转并电路
    • VL7 数据累加输出
    • VL8 非整数倍数据位宽转换24to128
    • VL9 非整数倍数据位宽转换8to12
    • VL10 整数倍数据位宽转换8to16
    • VL11 非重叠的序列检测
    • VL12 重叠序列检测
    • VL13 时钟分频(偶数)
    • VL14 自动贩售机1
    • VL15 自动贩售机2
    • VL16 占空比50%的奇数分频
    • VL17 任意小数分频
    • VL18 无占空比要求的奇数分频
    • VL19 根据状态转移写状态机-三段式
    • VL20 根据状态转移写状态机-两段式
    • VL21 异步FIFO
    • VL22 同步FIFO
    • VL23 格雷码计数器
    • VL24 多bit MUX同步器
    • VL25 脉冲同步电路
    • VL26 简易秒表
    • VL27 可置位计数器
    • VL28 加减计数器
    • VL29 单端口RAM
    • VL30 RAM的简单实现
    • VL31 Johnson Counter 约翰逊计数器
      • 解法1
      • 解法2
    • VL32 流水线乘法器
    • VL33 交通灯
      • 代码
      • TestBench
    • VL34 游戏机计费程序
  • 结束

视频讲解合集

FPGA数字IC刷题Verilog讲解
https://space.bilibili.com/507257163/channel/collectiondetail?sid=260755

刷题链接点击转到
https://www.nowcoder.com/exam/oj?tab=Verilog%E7%AF%87&topicId=302&fromPut=pc_zh_s_1540795715

入门篇1~24题

VL1 四选一多路器

讲解视频:FPGA数字IC牛客Verilog刷题01-四选一多路器

解法一 三目运算符

使用assign连续赋值语句 + 三目运算符 ? :

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module mux4_1(
input [1:0]d1,d2,d3,d0,
input [1:0]sel,
output [1:0]mux_out
);
//*************code***********//assign mux_out = (sel == 2'b00) ? d3 : ((sel == 2'b01) ? d2 : (sel == 2'b10) ? d1 : d0);//*************code***********//
endmodule
解法二 case语句

使用always过程赋值语句 + case语句

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module mux4_1(
input [1:0]d1,d2,d3,d0,
input [1:0]sel,
output [1:0]mux_out
);
//*************code***********//reg [1:0] mux_out_reg;
always @ (*)
begincase(sel)2'b00:mux_out_reg = d3;2'b01:mux_out_reg = d2;2'b10:mux_out_reg = d1;2'b11:mux_out_reg = d0;default : mux_out_reg = d0;endcase
end assign mux_out = mux_out_reg;//*************code***********//
endmodule
三目运算符?:

d = c ? a : b;

其中,a、b、c均可以是表达式也可以是变量等,等效于if…else语句。if…else只能在always语句描述中使用,所以有时候为了在描述组合逻辑时,一般就用?:来实现这种条件判断效果。

case语句
case(表达式)
条件分支1: xxx;
条件分支2: xxx;
...
条件分支n: xxx;
缺省default: xxx;
endcase

case的使用注意点:

(1)要在always块里使用,如果是用always块描述组合逻辑,注意括号里的敏感变量列表都是电平触发,并且赋值时都要用阻塞赋值“=”;

(2)always块里的变量必须声明成reg类型,当然声明成reg类型不代表一定会综合成寄存器,只是语法要求always块里要这样;

(3)always块描述组合逻辑时,用*可以代表所有always块内敏感信号;

(4)分支条件要写全,最好补齐default缺省条件,不然在组合逻辑中可能会由于条件不全导致出现锁存器Latch;

VL2 异步复位的串联T触发器

讲解视频:02-异步复位的串联T触发器

注意 T 触发器的概念,来 1 翻转,来 0 保持。
注意理解同步复位和异步复位。
联发科数字IC简答题(9)——异步复位同步释放问题

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module Tff_2 (input wire data, clk, rst,output reg q
);// 1. 复位
//2. T触发器,D触发器
//*************code***********//
reg q1;
always @ (posedge clk or negedge rst)
beginif(!rst) beginq1 <= 1'b0;end else beginif( data )q1 <= ~q1;else q1 <= q1;end
end always @ (posedge clk or negedge rst)
beginif(!rst) beginq <= 1'b0;end else beginif( q1 )q <= ~q;else q <= q;end
end //*************code***********//
endmodule

VL3 奇偶校验(实际上应该是奇偶检测)

讲解视频:03-奇偶校验

实际上这里做的是奇偶检测,如果是奇数个 1 则结果为 1,使用单目运算符 ^ 即可。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module odd_sel(input [31:0] bus,input sel,output check
);
//*************code***********//
wire check_tmp;// 单目运算符
assign check_tmp = ^bus;
//  assign check = (sel == 1'b1) ? check_tmp : ~check_tmp;reg check_reg;
always @ (*) beginif(sel) begincheck_reg = check_tmp;endelse begincheck_reg = ~check_tmp;end
end assign check = check_reg;//*************code***********//
endmodule

VL4 移位运算与乘法

讲解视频:04-移位拼接乘法

FSM 有限状态机思想,计数值就是状态。
注意在第一次获得数据的时候进行暂存。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module multi_sel(input [7:0]d ,input clk,input rst,output reg input_grant,output reg [10:0]out
);
//*************code***********//
reg [1:0] count;    // 0 1 2 3
always @ (posedge clk or negedge rst)
beginif(~rst) begincount <= 2'b0;end else begincount <= count + 1'b1;end
end // FSM 有限状态机思想,计数值就是状态
reg [7:0] d_reg;
always @ (posedge clk or negedge rst)
beginif(~rst) beginout <= 11'b0;input_grant <= 1'b0;d_reg <= 8'b0;end else begincase( count ) 2'b00 : beginout <= d;d_reg <= d;input_grant <= 1'b1;end 2'b01 : beginout <= d_reg + {d_reg, 1'b0};    // *1 + *2input_grant <= 1'b0;end 2'b10 : beginout <= d_reg + {d_reg, 1'b0} + {d_reg, 2'b0};input_grant <= 1'b0;end 2'b11 : begin out <= {d_reg, 3'b0};input_grant <= 1'b0;end default : begin out <= d;input_grant <= 1'b0;end endcaseend
end
//*************code***********//
endmodule

VL5 位拆分与运算

讲解视频05-位拆分与运算

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module data_cal(input clk,input rst,input [15:0]d,input [1:0]sel,output [4:0]out, // wireoutput validout // wire
);
//*************code***********//
reg [15:0] d_reg;
wire [3:0] d0;
wire [3:0] d1;
wire [3:0] d2;
wire [3:0] d3;
assign d0 = d_reg[3:0];
assign d1 = d_reg[7:4];
assign d2 = d_reg[11:8];
assign d3 = d_reg[15:12];reg [4:0] out_reg;
reg validout_reg;
always @ (posedge clk or negedge rst)beginif( ~rst ) beginout_reg <= 5'b0;validout_reg <= 1'b0;d_reg <= 16'b0;end else begincase( sel ) 2'b00 : begind_reg <= d;out_reg <= 5'b0;validout_reg <= 1'b0;    end 2'b01 : begind_reg <= d_reg;out_reg <= d_reg[3:0] + d_reg[7:4];// d0 + d1;validout_reg <= 1'b1;    end 2'b10 : begind_reg <= d_reg;out_reg <= d0 + d2;validout_reg <= 1'b1;    end 2'b11 : begind_reg <= d_reg;out_reg <= d0 + d3;validout_reg <= 1'b1;    end default : beginout_reg <= 5'b0;validout_reg <= 1'b0;  end endcaseend endassign out = out_reg;
assign validout = validout_reg;//*************code***********//
endmodule

VL6 多功能数据处理器

讲解视频:06-多功能数据处理器

题解 | Verilog刷题解析及对应笔试面试注意点【6-9】(涉及==和===、for展开问题等)

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module data_select(input clk,input rst_n,input signed[7:0]a,input signed[7:0]b,input [1:0]select,output reg signed [8:0]c
);always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginc <= 9'b0;end else begincase ( select )2'b00 : beginc <= {a[7], a};end 2'b01 : beginc <= {b[7], b};end 2'b10 : beginc <= {a[7], a} + {b[7], b};end 2'b11 : beginc <= {a[7], a} - {b[7], b};end default : beginc <= 9'b0;end endcaseend
end     endmodule

VL7 求两个数的差值

讲解视频:07-if…else

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module data_minus(input clk,input rst_n,input [7:0]a,input [7:0]b,output  reg [8:0]c
);always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginc <= 8'b0;end else beginif( a > b ) beginc <= a - b;end else beginc <= b - a;end end
end    endmodule

VL8 使用generate…for语句简化代码

讲解视频:08-generate_for和for

分别给出generate…for和for的代码,可以对比不同点。

使用generate…for
`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module gen_for_module( input [7:0] data_in,output [7:0] data_out
);// 1. 必须使用 genvar 声明循环变量
// begin后面必须起个名字
genvar ii;
generate for(ii = 0; ii < 8; ii = ii+1) begin : aaa_iassign data_out[ii] = data_in[7-ii];end
endgenerateendmodule
使用for
`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module gen_for_module( input [7:0] data_in,output [7:0] data_out
);// 2. for
integer i;
reg [7:0] dout_reg;
always @ (*) beginfor(i = 0; i < 8; i = i+1) begin dout_reg[i] = data_in[7-i];end
end assign data_out = dout_reg;
endmodule

VL9 使用子模块实现三输入数的大小比较

讲解视频:09-子模块例化

解法一 使用时序逻辑子模块

需要调用3个模块,这里很多同学可能疑惑为什么用3个而不是2个。

第一个模块:比较 T 时刻的 a 和 b,T+1 时刻出来 tmp1;
第二个模块:比较 T 时刻的 a 和 c,T+1 时刻出来 tmp2;
第三个模块:比较 T+1 时刻的 tmp1 和 tmp2,T+2 时刻出来 d;

如果只用2个子模块,那么 T 时刻比较 a 和 b 得到 tmp1,再比较 tmp1 和 c 的时候是 T+1 时刻的 c 和 T+1 时刻的 tmp1,而 tmp1 代表的是 T 时刻 a 和 b 的较小值,所以这时候比较的 T 时刻的 a、b 和 T+1 时刻的 c,显然不符合要求。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module main_mod(input clk,input rst_n,input [7:0]a,input [7:0]b,input [7:0]c,output [7:0]d
);// T 时刻的 a 和 b,T+1 时刻出来 tmp1
wire [7:0] tmp1;    // a b 的最小值
child_mod U0(.clk      ( clk ),.rst_n    ( rst_n ),.a        ( a ),.b        ( b ),.d        ( tmp1 )
);// T 时刻的 a 和 c,T+1 时刻出来 tmp2
wire [7:0] tmp2;    // a c 的最小值
child_mod U1(.clk      ( clk ),.rst_n    ( rst_n ),.a        ( a ),.b        ( c ),.d        ( tmp2 )
);// T+1 时刻的 tmp1 和 tmp2,T+2 时刻出来 d
child_mod U2(.clk      ( clk ),.rst_n    ( rst_n ),.a        ( tmp1 ),.b        ( tmp2 ),.d        ( d )
);endmodule// 子模块
module child_mod(input clk,input rst_n,input [7:0]a,input [7:0]b,output [7:0]d
);reg [7:0] d_reg;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begind_reg <= 8'b0;end else beginif( a > b )d_reg <= b;elsed_reg <= a;end
end
assign d = d_reg;endmodule
解法二 使用组合逻辑子模块,需要打两拍

组合逻辑的子模块,就不存在时序逻辑中的延时问题,所以调用的时候用2个子模块就可以完成3个数的比较,为了符合时序波形的要求,多打一拍。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module main_mod(input clk,input rst_n,input [7:0]a,input [7:0]b,input [7:0]c,output [7:0]d
);wire [7:0] tmp1;    // a b 的最小值
child_mod U0(.a        ( a ),.b        ( b ),.d        ( tmp1 )
);wire [7:0] tmp2;    // a c 的最小值
child_mod U1(.a        ( tmp1 ),.b        ( c ),.d        ( tmp2 )
);reg [7:0] d_reg;
reg [7:0] d_reg2;
always @ (posedge clk or negedge rst_n)beginif( ~rst_n ) begind_reg <= 8'b0;d_reg2 <= 8'b0;end else begind_reg <= tmp2;d_reg2 <= d_reg;end endassign d = d_reg2;endmodulemodule child_mod(input [7:0]a,input [7:0]b,output [7:0]d
);assign d = (a>b) ? b : a;endmodule

VL10 使用函数实现数据大小端转换

讲解视频:10-函数与任务function和task

题解 | Verilog刷题解析【10】function和task的使用、相关笔试题

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module function_mod(input clk,input rst_n,input [3:0]a,input [3:0]b,output [3:0]c,output [3:0]d
);/*
function <返回值的类型或范围>函数名;<端口说明语句><变量类型说明语句>begin<语句>end
endfunction
*/
function [3:0] begin_end;input [3:0] data_in;beginbegin_end[0] = data_in[3];begin_end[1] = data_in[2];begin_end[2] = data_in[1];begin_end[3] = data_in[0];end
endfunctionassign c = begin_end(a);
assign d = begin_end(b);endmodule

VL11 4位数值比较器电路

讲解视频:11~20-组合逻辑优先编码

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module comparator_4(input  [3:0]    A       ,input  [3:0]    B      ,output  wire    Y2    , //A>Boutput  wire    Y1    , //A=Boutput  wire    Y0      //A<B
);assign Y2 = (A[3]>B[3]) | ((A[3]==B[3])&&(A[2]>B[2])) | ((A[3]==B[3])&&(A[2]==B[2])&&(A[1]>B[1])) | ((A[3]==B[3])&&(A[2]==B[2])&&(A[1]==B[1])&&(A[0]>B[0]));
assign Y1 = (A[3]==B[3])&&(A[2]==B[2])&&(A[1]==B[1])&&(A[0]==B[0]);
assign Y0 = (~Y2) & (~Y1);endmodule

VL12 4bit超前进位加法器电路

讲解视频:11~20-组合逻辑优先编码

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module lca_4(input  [3:0]    A_in  ,input  [3:0]    B_in  ,input           C_1   ,output  wire          CO    ,output  wire [3:0]    S
);wire [3:0] G;
wire [3:0] P;assign G[0] = A_in[0] & B_in[0];
assign G[1] = A_in[1] & B_in[1];
assign G[2] = A_in[2] & B_in[2];
assign G[3] = A_in[3] & B_in[3];assign P[0] = A_in[0] ^ B_in[0];
assign P[1] = A_in[1] ^ B_in[1];
assign P[2] = A_in[2] ^ B_in[2];
assign P[3] = A_in[3] ^ B_in[3];wire [3:0] C;assign S[0] = P[0] ^ C_1;
assign S[1] = P[1] ^ C[0];
assign S[2] = P[2] ^ C[1];
assign S[3] = P[3] ^ C[2];
assign CO = C[3];assign C[0] = G[0] | P[0]&C_1;
assign C[1] = G[1] | P[1]&C[0];
assign C[2] = G[2] | P[2]&C[1];
assign C[3] = G[3] | P[3]&C[2];endmodule

1320不建议做了,没有太大意义,这里只给出1316的代码。

VL13 优先编码器电路1

讲解视频:11~20-组合逻辑优先编码

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module encoder_0(input      [8:0] I_n   ,   output reg [3:0] Y_n
);always @ (*) begin casex(I_n)9'b1_1111_1111 : Y_n = 4'b1111;9'b0_xxxx_xxxx : Y_n = 4'b0110;9'b1_0xxx_xxxx : Y_n = 4'b0111;9'b1_10xx_xxxx : Y_n = 4'b1000;9'b1_110_xxxx : Y_n = 4'b1001;9'b1_1110_xxxx : Y_n = 4'b1010;9'b1_1111_0xxx : Y_n = 4'b1011;9'b1_1111_10xx : Y_n = 4'b1100;9'b1_1111_110x : Y_n = 4'b1101;9'b1_1111_1110 : Y_n = 4'b1110;default : Y_n = 4'b1111;endcaseend endmodule

VL14 用优先编码器1实现键盘编码电路

讲解视频:11~20-组合逻辑优先编码

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module encoder_0(input      [8:0]         I_n   ,   output reg [3:0]         Y_n
);always @(*)
begincasex(I_n)9'b111111111 : Y_n = 4'b1111;9'b0xxxxxxxx : Y_n = 4'b0110;9'b10xxxxxxx : Y_n = 4'b0111;9'b110xxxxxx : Y_n = 4'b1000;9'b1110xxxxx : Y_n = 4'b1001;9'b11110xxxx : Y_n = 4'b1010;9'b111110xxx : Y_n = 4'b1011;9'b1111110xx : Y_n = 4'b1100;9'b11111110x : Y_n = 4'b1101;9'b111111110 : Y_n = 4'b1110;default      : Y_n = 4'b1111;endcase
end endmodulemodule key_encoder(input      [9:0]         S_n   ,    output wire[3:0]         L     ,output wire              GS
);wire [3:0] LL;
encoder_0  U0(.I_n( S_n[9:1] )   ,.Y_n( LL )
); assign L = ~LL;
assign GS = ((LL == 4'b1111) && (S_n[0] == 1)) ? 0 : 1;endmodule

VL15 优先编码器2——8线-3线优先编码器

讲解视频:11~20-组合逻辑优先编码

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module encoder_83(input      [7:0]       I   ,input                  EI  ,  output wire [2:0]      Y   ,output wire            GS  ,output wire            EO
);reg [2:0] Y_Reg;
reg GS_Reg;
reg EO_Reg;
always @ (*)
begin if( EI == 1'b0 ) begin Y_Reg = 3'b0;GS_Reg = 1'b0;EO_Reg = 1'b0;end else begincasex(I)8'b0000_0000 : beginY_Reg = 3'b0;GS_Reg = 1'b0;EO_Reg = 1'b1;                        end 8'b1xxx_xxxx : beginY_Reg = 3'b111;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b01xx_xxxx : beginY_Reg = 3'b110;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b001x_xxxx : beginY_Reg = 3'b101;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b0001_xxxx : beginY_Reg = 3'b100;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b0000_1xxx : beginY_Reg = 3'b011;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b0000_01xx : beginY_Reg = 3'b010;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b0000_001x : beginY_Reg = 3'b001;GS_Reg = 1'b1;EO_Reg = 1'b0;                        end8'b0000_0001 : beginY_Reg = 3'b000;GS_Reg = 1'b1;EO_Reg = 1'b0;                        endendcaseend
end assign Y = Y_Reg;
assign GS = GS_Reg;
assign EO = EO_Reg;endmodule

VL16 使用8线-3线优先编码器实现16线-4线优先编码器

讲解视频:11~20-组合逻辑优先编码

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module encoder_83(input      [7:0]       I   ,input                  EI  ,output wire [2:0]      Y   ,output wire            GS  ,output wire            EO
);
assign Y[2] = EI & (I[7] | I[6] | I[5] | I[4]);
assign Y[1] = EI & (I[7] | I[6] | ~I[5]&~I[4]&I[3] | ~I[5]&~I[4]&I[2]);
assign Y[0] = EI & (I[7] | ~I[6]&I[5] | ~I[6]&~I[4]&I[3] | ~I[6]&~I[4]&~I[2]&I[1]);assign EO = EI&~I[7]&~I[6]&~I[5]&~I[4]&~I[3]&~I[2]&~I[1]&~I[0];assign GS = EI&(I[7] | I[6] | I[5] | I[4] | I[3] | I[2] | I[1] | I[0]);
//assign GS = EI&(| I);endmodulemodule encoder_164(input      [15:0]      A   ,input                  EI  ,output wire [3:0]      L   ,output wire            GS  ,output wire            EO
);wire [2:0] Y_1;
wire GS_1;
wire EO_1;
encoder_83 U0(.I(A[15:8])   ,.EI(EI)  ,.Y(Y_1)   ,.GS(GS_1)  ,.EO(EO_1)
);wire [2:0] Y_0;
wire GS_0;
wire EO_0;
encoder_83 U1(.I(A[7:0])   ,.EI(EO_1)  ,   .Y(Y_0)   ,.GS(GS_0)  ,.EO(EO_0)
);assign GS = GS_1 | GS_0;
assign EO = EO_0;
assign L[3] = GS_1;
assign L[2] = Y_1[2] | Y_0[2];
assign L[1] = Y_1[1] | Y_0[1];
assign L[0] = Y_1[0] | Y_0[0];endmodule

VL17~20 不建议做

讲解视频:11~20-组合逻辑优先编码

VL21 根据状态转移表实现时序电路

讲解视频:21-FSM有限状态机时序逻辑(1)
FSM有限状态机问题,两段式。

  • 摩尔型:输出只与当前状态有关;
  • 米利型;输出不仅与当前状态有关,还有当前输入有关;

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module seq_circuit(input                A   ,input                clk ,input                rst_n,output   wire        Y
);reg [1:0] curr_state;
reg [1:0] next_state;
// one step
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincurr_state <= 2'b00;next_state <= 2'b00;end else begincurr_state <= next_state;end
end // two step
always @ (*)
begincase(curr_state)2'b00 : next_state = (A == 1'b1) ? 2'b11 : 2'b01;2'b01 : next_state = (A == 1'b1) ? 2'b00 : 2'b10;2'b10 : next_state = (A == 1'b1) ? 2'b01 : 2'b11;2'b11 : next_state = (A == 1'b1) ? 2'b10 : 2'b00;default : next_state = 2'b00;endcase
endassign Y = (curr_state == 2'b11) ? 1 : 0;endmodule

VL22 使用状态转移图实现时序电路

讲解视频:22-FSM状态机时序逻辑(2)

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module seq_circuit(input                C   ,input                clk ,input                rst_n,output   wire        Y
);reg [1:0] curr_state;
reg [1:0] next_state;
// one step
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincurr_state <= 2'b00;next_state <= 2'b00;end else begincurr_state <= next_state;end
end // two step
always @ (*)
begincase(curr_state)2'b00 : next_state = (C == 1'b1) ? 2'b01 : 2'b00;2'b01 : next_state = (C == 1'b1) ? 2'b01 : 2'b11;2'b10 : next_state = (C == 1'b1) ? 2'b10 : 2'b00;2'b11 : next_state = (C == 1'b1) ? 2'b10 : 2'b11;default : next_state = 2'b00;endcase
endassign Y = ((curr_state == 2'b11) | ((curr_state == 2'b10)&&(C == 1'b1)) )? 1 : 0;endmodule

VL23 ROM的简单实现

讲解视频:23-ROM

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module rom(input clk,input rst_n,input [7:0]addr,output [3:0]data
);reg [3:0] romreg[7:0];
integer i;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginromreg[0] <= 4'd0;romreg[1] <= 4'd2;romreg[2] <= 4'd4;romreg[3] <= 4'd6;romreg[4] <= 4'd8;romreg[5] <= 4'd10;romreg[6] <= 4'd12;romreg[7] <= 4'd14;end else begin// romreg[0] <= romreg[0];// ...// romreg[7] <= romreg[7];for(i = 0; i < 8; i = i+1) begin : rom_iromreg[i] <= romreg[i];end end
endassign data = romreg[addr];endmodule

VL24 边沿检测

讲解视频:24-边沿检测_上升沿检测_下降沿检测

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module edge_detect(input clk,input rst_n,input a,output reg rise,output reg down
);reg a1;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begina1 <= 1'b0;rise <= 1'b0;down <= 1'b0;end else begina1 <= a;if(a & ~a1)rise <= 1'b1;else rise <= 1'b0;if(~a & a1)down <= 1'b1;elsedown <= 1'b0;end
end endmodule

刷题地址:

进阶篇1~34题

VL1 输入序列连续的序列检测

讲解视频:进阶版01-状态机序列检测

三段式状态机解决。

  • 第一段:时序逻辑,固定写法,cs <= ns;
  • 第二段:组合逻辑,描述状态跳转,cs 和输入下的 ns 变化;
  • 第三段:时序逻辑,描述输出。
`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module sequence_detect (input clk,input rst_n,input a,output reg match
);// 独热码编码 只有1 bit是1
parameter IDLE   = 9'b000000001;
parameter S0     = 9'b000000010;
parameter S1     = 9'b000000100;
parameter S2     = 9'b000001000;
parameter S3     = 9'b000010000;
parameter S4     = 9'b000100000;
parameter S5     = 9'b001000000;
parameter S6     = 9'b010000000;
parameter S7     = 9'b100000000;reg [8:0] cs, ns;
// one step
always @ (posedge clk or negedge rst_n)
begin if( ~rst_n ) begincs <= IDLE;end else begin cs <= ns;end
end //  two step
always @ (*)
begincase(cs)IDLE : ns = (a == 1'b0) ? S0 : IDLE;S0   : ns = (a == 1'b1) ? S1 : S0; S1   : ns = (a == 1'b1) ? S2 : S0;S2   : ns = (a == 1'b1) ? S3 : S0;S3   : ns = (a == 1'b0) ? S4 : S0;S4   : ns = (a == 1'b0) ? S5 : S0;S5   : ns = (a == 1'b0) ? S6 : S0;S6   : ns = (a == 1'b1) ? S7 : S0;S7   : ns = (a == 1'b1) ? IDLE : S0;default : ns = IDLE;endcase
end wire match_tmp;
assign match_tmp = (cs == S7);// three step
always @ (posedge clk or negedge rst_n)
begin if( ~rst_n ) beginmatch <= 1'b0;end else begin match <= match_tmp;end
end endmodule

VL2 还有无关项的序列检测

讲解视频:进阶版02-无关项序列检测-移位寄存器-casex-计数器

寄存器存储比较。也可以考虑用状态机。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module sequence_detect (input clk,input rst_n,input a,output reg match
);reg [3:0] count;
reg  [8:0] data;
reg flag;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begin count <= 4'b0;flag <= 1'b0;data <= 9'b0;end else begin if( count == 4'd8) begincount <= 4'd0;data[count] <= a;flag <= 1'b1;end else begincount <= count + 4'd1;data[count] <= a;flag <= 1'b0;end end
end wire match_tmp;
assign match_tmp = (flag == 1'b1) && (data[8:6] == 3'b011) && (data[2:0] == 3'b110);always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begin match <= 1'b0;end else begin match <= match_tmp;end
end
endmodule

VL3 不重叠序列检测

讲解视频:进阶版03-不重叠序列检测

计数器其实就是一个简化的状态机模型,不同的计数值就是不同的状态。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module sequence_detect (input clk,input rst_n,input data,output reg match,output reg not_match
);reg [2:0] count;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincount <= 3'b0;endelse beginif(count == 3'd5) count <= 3'b0;elsecount <= count + 1'b1;end
end reg [5:0] seq;
always @ (*)
begincase(count) 3'd0 : seq[5] = data;3'd1 : seq[4] = data;3'd2 : seq[3] = data;3'd3 : seq[2] = data;3'd4 : seq[1] = data;3'd5 : seq[0] = data;default : seq = 6'b0;endcase
end always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginmatch <= 1'b0;not_match <= 1'b0;endelse beginif(count == 3'd5) beginif(seq == 6'b011100) beginmatch <= 1'b1;not_match <= 1'b0;end else beginmatch <= 1'b0;not_match <= 1'b1;end end else beginmatch <= 1'b0;not_match <= 1'b0;end end
endendmodule

VL4 输入序列不连续的序列检测

讲解视频:进阶版04-不连续的序列检测

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module sequence_detect(input clk,input rst_n,input data,input data_valid,output reg match
);// one step
reg [1:0] cs, ns;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincs <= 2'b0;endelse begincs <= ns;end
end // two step
always @ (*)
begincase(cs) 2'b00 : beginif(data_valid) beginns = (data==1'b0) ? 2'b01 : 2'b00;end else beginns = cs;end end2'b01 : beginif(data_valid) beginns = (data==1'b1) ? 2'b10 : 2'b01;endelse beginns = cs;end end2'b10 : beginif(data_valid) beginns = (data==1'b1) ? 2'b11 : 2'b00;end else beginns = cs;end end2'b11 : beginif(data_valid) beginns = 2'b00;endelse beginns = cs;end endendcase
end// three step
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginmatch <= 1'b0;endelse beginif(cs == 2'b11 && data == 1'b0 && data_valid == 1'b1)match <= 1'b1;elsematch <= 1'b0;end
end endmodule

VL5 信号发生器

讲解视频:进阶版05-信号发生器

题目有一些问题,看题解或者视频才能做。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module signal_generator(input clk,input rst_n,input [1:0] wave_choise,output reg [4:0]wave);reg [4:0] count; // 计数0~19
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincount <= 5'b0;end else beginif(wave_choise == 2'b00) beginif(count == 5'd19) begincount <= 5'b0;end else begincount <= count + 1'b1;end endend
end reg flag;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginwave <= 5'b0;flag <= 1'b1; //指示 开始阶段的三角波处在下降end else begincase(wave_choise)2'b00 : beginif(count == 5'd9) beginwave <= 5'd20;end else if(count >= 5'd19)beginwave <= 5'd0;end else beginwave <= wave;end end 2'b01 : beginif(wave >= 5'd20) beginwave <= 5'b0;end else beginwave <= wave + 1'b1;end end 2'b10 : beginif(flag == 1'b0) beginif(wave >= 5'd20) beginflag <= 1'b1;wave <= wave - 1'b1;end else beginwave <= wave + 1'b1;end end else beginif(wave == 5'd0) beginflag <= 1'b0;wave <= wave + 1'b1;end else beginwave <= wave - 1'b1;end end end default : wave <= 5'b0;endcaseend
end endmodule

VL6 数据串转并电路

讲解视频:进阶版06-串并转换

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module s_to_p(input                 clk         ,   input               rst_n       ,input              valid_a     ,input              data_a      , output    reg         ready_a     ,output reg         valid_b     ,output  reg [5:0]  data_b
);reg [2:0] count;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincount <= 3'b0;ready_a <= 1'b0;end else beginready_a <= 1'b1;if(valid_a) beginif(count == 3'd5) begincount <= 3'd0;end else begincount <= count + 3'd1;end end end
end reg [5:0] data_b_reg;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begindata_b <= 6'b0;valid_b <= 1'b0;data_b_reg <= 6'b0;end else beginif(valid_a) begindata_b_reg <= {data_a, data_b_reg[5:1]};if(count == 3'd5) beginvalid_b <= 1'd1;data_b <= {data_a, data_b_reg[5:1]};end else beginvalid_b <= 1'd0;data_b <= data_b;endend end
endendmodule

VL7 数据累加输出

没录制视频,多理解一下 valid 和 ready 信号、流水线反压。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module valid_ready(input          clk       ,   input          rst_n        ,input  [7:0]     data_in       ,input          valid_a     ,input          ready_b     ,output          ready_a        ,output  reg      valid_b       ,output  reg [9:0]  data_out
);reg [1:0] count;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincount <= 2'b0;end else beginif(valid_a & ready_a) beginif( count == 2'd3)count <= 2'd0;else count <= count + 2'd1;end end
end always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begindata_out <= 10'b0;end else beginif(valid_a && ready_a) beginif(count == 2'd0) begindata_out <= data_in;endelse begindata_out <= data_out + data_in;end end end
end always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) beginvalid_b <= 1'b0;end else beginif(count == 2'd3 && valid_a && ready_a) beginvalid_b <= 1'b1;end else if(valid_b && ready_b)beginvalid_b <= 1'b0;end else beginvalid_b <= valid_b;end end
end assign ready_a = ~valid_b | ready_b;endmodule

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

讲解视频:进阶版08-非整数倍数据位宽转换24to128

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module width_24to128(input              clk         ,   input               rst_n       ,input              valid_in    ,input  [23:0]      data_in     ,output reg         valid_out   ,output  reg [127:0]    data_out
);reg [3:0] cnt;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincnt <= 4'd0;end else beginif( valid_in ) begincnt <= cnt + 4'd1;end end
endreg [15:0] data_reg;
reg [127:0] data_out_r;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begindata_out <= 128'b0;valid_out <= 1'b0;data_out_r <= 128'b0;end else beginif(valid_in) begincase(cnt)4'd0 : begindata_out_r[127:104] <= data_in;valid_out <= 1'b0;end4'd1 : begindata_out_r[103:80] <= data_in;valid_out <= 1'b0;end4'd2 : begindata_out_r[79:56] <= data_in;valid_out <= 1'b0;end4'd3 : begindata_out_r[55:32] <= data_in;valid_out <= 1'b0;end4'd4 : begindata_out_r[31:8] <= data_in;valid_out <= 1'b0;end4'd5 : begindata_out <= {data_out_r[127:8], data_in[23:16]};data_out_r[127:112] <= data_in[15:0];valid_out <= 1'b1;end4'd6 : begindata_out_r[111:88] <= data_in[23:0];valid_out <= 1'b0;end4'd7 : begindata_out_r[87:64] <= data_in[23:0];valid_out <= 1'b0;end4'd8 : begindata_out_r[63:40] <= data_in[23:0];valid_out <= 1'b0;end4'd9 : begindata_out_r[39:16] <= data_in[23:0];valid_out <= 1'b0;end4'd10 : begindata_out <= {data_out_r[127:16], data_in[23:8]};data_out_r[127:120] <= data_in[7:0];valid_out <= 1'b1;end4'd11 : begindata_out_r[119:96] <= data_in[23:0];valid_out <= 1'b0;end4'd12 : begindata_out_r[95:72] <= data_in[23:0];valid_out <= 1'b0;end4'd13 : begindata_out_r[71:48] <= data_in[23:0];valid_out <= 1'b0;end4'd14 : begindata_out_r[47:24] <= data_in[23:0];valid_out <= 1'b0;end4'd15 : begindata_out_r[23:0] <= data_in[23:0];data_out <= {data_out_r[127:24], data_in[23:0]};valid_out <= 1'b1;endendcaseendelse beginvalid_out <= 1'b0;end end
endendmodule

VL9 非整数倍数据位宽转换8to12

讲解视频:进阶版09~10-非整数倍数据位宽转换

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module width_8to12(input                   clk      ,   input                 rst_n     ,input                    valid_in  ,input  [7:0]              data_in  ,output  reg               valid_out,output  reg [11:0]   data_out
);reg [1:0] cnt;
always @ (posedge clk or negedge rst_n) beginif(~rst_n) begincnt <= 2'b0;end else beginif(valid_in) beginif(cnt == 2'd2) cnt <= 2'd0;else cnt <= cnt + 2'd1;end end
end reg [7:0] data_reg;
always @ (posedge clk or negedge rst_n) beginif(~rst_n) begindata_out <= 12'b0;valid_out <= 1'b0;data_reg <= 8'b0;end else beginif(valid_in) begincase(cnt) 2'd0 : begindata_reg <= data_in;valid_out <= 1'b0;end2'd1 : begindata_out <= {data_reg[7:0], data_in[7:4]};data_reg[3:0] <= data_in[3:0];valid_out <= 1'b1;end 2'd2 : begindata_out <= {data_reg[3:0], data_in[7:0]};valid_out <= 1'b1;end default : begindata_reg <= 8'b0;data_out <= 12'b0;valid_out <= 1'b0;end endcase    end else beginvalid_out <= 1'b0;end end
end endmodule

VL10 整数倍数据位宽转换8to16

和VL9是一起的,讲解视频:进阶版09~10-非整数倍数据位宽转换

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module width_8to16(input                   clk      ,   input                  rst_n        ,input                    valid_in  ,input     [7:0]           data_in  ,output reg         valid_out,output   reg [15:0]   data_out
);reg cnt;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) begincnt <= 1'b0;end else beginif(valid_in) begincnt <= cnt + 1'b1; // cnt = ~cnt;end end
end reg [7:0] data_reg;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) begindata_out <= 16'b0;valid_out <= 1'b0;end else beginif(valid_in) beginif(cnt == 1'b0) begindata_reg <= data_in;valid_out <= 1'b0;end else begindata_out <= {data_reg, data_in};valid_out <= 1'b1;end end else beginvalid_out <= 1'b0;end end
endendmodule

VL11 非重叠的序列检测

讲解视频:进阶版11-非重叠序列检测-状态机

检测序列10111,非重叠检测,即 101110111 只会被检测通过一次。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module sequence_test1(input wire clk  ,input wire rst  ,input wire data ,output reg flag
);
//*************code***********//
// one-hot独热码
parameter IDLE = 6'b000001;
parameter S0   = 6'b000010;
parameter S1   = 6'b000100;
parameter S2   = 6'b001000;
parameter S3   = 6'b010000;
parameter S4   = 6'b100000;// one step
reg [5:0] cs;
reg [5:0] ns;
always @ (posedge clk or negedge rst)
beginif(~rst) begincs <= IDLE;end else begincs <= ns;end
end // two step
always @ (*)
begincase(cs) IDLE  : ns = (data == 1'b1) ? S0 : IDLE;S0    : ns = (data == 1'b0) ? S1 : S0;S1    : ns = (data == 1'b1) ? S2 : IDLE;S2    : ns = (data == 1'b1) ? S3 : S1;S3    : ns = (data == 1'b1) ? S4 : S1;S4    : ns = (data == 1'b1) ? S0 : IDLE;default : ns = IDLE;endcase
end //three step
always @ (posedge clk or negedge rst)
beginif(~rst) beginflag <= 1'b0;end else begin//if(ns == S4) beginif(cs == S3 && data == 1'b1) beginflag <= 1'b1;end else beginflag <= 1'b0;end end
end//*************code***********//
endmodule

VL12 重叠序列检测

讲解视频:进阶版12-重叠序列检测-状态机-寄存器输出时延Tco

检测序列 1011,进行重叠检测,即 10110111 会被检测通过 2 次。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module sequence_test2(input wire clk  ,input wire rst  ,input wire data ,output reg flag
);
//*************code***********//
parameter IDLE = 5'b00001;
parameter S0   = 5'b00010;
parameter S1   = 5'b00100;
parameter S2   = 5'b01000;
parameter S3   = 5'b10000;// one step
reg [4:0] cs;
reg [4:0] ns;
always @ (posedge clk or negedge rst) beginif(~rst) begincs <= IDLE;end else begincs <= ns;end
end // two step
always @ (*) begincase(cs) IDLE  : ns = (data == 1'b1) ? S0 : IDLE;S0    : ns = (data == 1'b0) ? S1 : S0;S1    : ns = (data == 1'b1) ? S2 : IDLE;S2    : ns = (data == 1'b1) ? S3 : S1;S3    : ns = (data == 1'b1) ? S0 : S1;default : ns = IDLE;endcase
end //three step
always @ (posedge clk or negedge rst) beginif(~rst) beginflag <= 1'b0;end else beginif(cs == S3) beginflag <= 1'b1;end else beginflag <= 1'b0;end end
endendmodule

VL13 时钟分频(偶数)

讲解视频:进阶版13-偶数分频

为了匹配题目给的参考波形,这里的计数器用了减法计数。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module even_div(input     wire rst ,input     wire clk_in,output    wire clk_out2,output    wire clk_out4,output    wire clk_out8);
//*************code***********//
reg [2:0] cnt8;
always @ (posedge clk_in or negedge rst) beginif(~rst) begincnt8 <= 3'b0;end else begincnt8 <= cnt8 - 3'd1;end
end assign clk_out2 = cnt8[0];
assign clk_out4 = cnt8[1];
assign clk_out8 = cnt8[2];endmodule

VL14 自动贩售机1

讲解视频:进阶版14-自动售货机(米利型和摩尔型Mealy和Moore)

典型状态机问题。米利型和摩尔型两种解决方式。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
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
);
//*************code***********//
parameter IDLE = 3'b001;
parameter S0   = 3'b010;
parameter S1   = 3'b100;reg [2:0] cs, ns;
always @ (posedge clk or negedge rst)
beginif( ~rst ) begincs <= IDLE;endelse begincs <= ns;end
end always @ (d1, d2, d3, rst)
beginif( ~rst ) beginns = IDLE;end else begincase( cs )IDLE : begincase({d1, d2, d3}) 3'b000 : ns = ns;3'b100 : ns = S0;3'b010 : ns = S1;3'b001 : ns = IDLE;default : ns = IDLE;endcaseend S0 : begincase({d1, d2, d3}) 3'b000 : ns = ns;3'b100 : ns = S1;3'b010 : ns = IDLE;3'b001 : ns = IDLE;default : ns = IDLE;endcaseend S1 : beginif({d1, d2, d3} == 3'b000)ns = ns;else ns = IDLE;     enddefault : beginif({d1, d2, d3} == 3'b000)ns = ns;else ns = IDLE;  end endcaseend
end reg out1_reg;
reg [1:0] out2_reg;
always @ (posedge clk or negedge rst)
beginif( ~rst ) beginout1_reg <= 1'b0;out2_reg <= 2'b0;endelse begincase(cs) IDLE : beginif({d1, d2, d3} == 3'b001) beginout1_reg <= 1'b1; out2_reg <= 2'd1; end     else beginout1_reg <= 1'b0; out2_reg <= 2'd0;end end S0 : begincase({d1, d2, d3}) 3'b010 : beginout1_reg <= 1'b1; out2_reg <= 2'd0; end 3'b001 : beginout1_reg <= 1'b1; out2_reg <= 2'd2; end default : beginout1_reg <= 1'b0; out2_reg <= 2'd0; end endcaseend S1 : begincase({d1, d2, d3}) 3'b100 : beginout1_reg <= 1'b1; out2_reg <= 2'd0; end 3'b010 : beginout1_reg <= 1'b1; out2_reg <= 2'd1; end 3'b001 : beginout1_reg <= 1'b1; out2_reg <= 2'd3; end default : beginout1_reg <= 1'b0; out2_reg <= 2'd0; end endcase     enddefault : beginout1_reg <= 1'b0; out2_reg <= 2'd0;end endcaseend
end always @ (posedge clk or negedge rst)
beginif( ~rst ) beginout1 <= 1'b0;out2 <= 2'b0;endelse beginout1 <= out1_reg;out2 <= out2_reg;end
endendmodule

VL15 自动贩售机2

讲解视频:进阶版15-自动贩售机2

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module seller2(input wire clk  ,input wire rst  ,input wire d1 ,input wire d2 ,input wire sel , output reg out1,output reg out2,output reg out3
);
//*************code***********//
parameter IDLE = 7'b0000001;
parameter S05  = 7'b0000010;
parameter S10  = 7'b0000100;
parameter S15  = 7'b0001000;
parameter S20  = 7'b0010000;
parameter S25  = 7'b0100000;
parameter S30  = 7'b1000000;reg [6:0] cs, ns;
always @ (posedge clk or negedge rst)
beginif( ~rst ) begincs <= IDLE;endelse begincs <= ns;end
end always @ (d1, d2, sel, rst, cs)
beginif( ~rst ) beginns = IDLE;end else begincase( cs )IDLE : begincase({d1, d2}) 2'b10 : ns = S05;2'b01 : ns = S10;default : ns = ns;endcaseend S05 : begincase({d1, d2}) 2'b10 : ns = S10;2'b01 : ns = S15;default : ns = ns;endcaseend S10 : begincase({d1, d2}) 2'b10 : ns = S15;2'b01 : ns = S20;default : ns = ns;endcase     endS15 : begin// 1.5 还是 2.5if(sel == 1'b0) beginns = IDLE;endelse begincase({d1, d2}) 2'b10 : ns = S20;2'b01 : ns = S25;default : ns = ns;endcaseend endS20 : begin// 1.5 还是 2.5if(sel == 1'b0) beginns = IDLE;endelse begincase({d1, d2}) 2'b10 : ns = S25;2'b01 : ns = S30;default : ns = ns;endcaseend endS25 : beginns = IDLE; endS30 : beginns = IDLE;  enddefault : beginns = IDLE;  end endcaseend
end always @ (posedge clk or negedge rst)
beginif( ~rst ) beginout1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;endelse begincase(ns) IDLE : beginout1 <= 1'b0; out2 <= 1'b0;out3 <= 1'b0;end S05 : beginout1 <= 1'b0; out2 <= 1'b0;out3 <= 1'b0;end S10 : beginout1 <= 1'b0; out2 <= 1'b0;out3 <= 1'b0; endS15 : beginif(sel == 1'b0) beginout1 <= 1'b1; out2 <= 1'b0;out3 <= 1'b0; end else beginout1 <= 1'b0; out2 <= 1'b0;out3 <= 1'b0; end endS20 : beginif(sel == 1'b0) beginout1 <= 1'b1; out2 <= 1'b0;out3 <= 1'b1; end else beginout1 <= 1'b0; out2 <= 1'b0;out3 <= 1'b0; end endS25 : beginif(sel == 1'b0) beginout1 <= 1'b0; out2 <= 1'd0;out3 <= 1'b0; end else beginout1 <= 1'b0; out2 <= 1'd1;out3 <= 1'b0; end endS30 : beginif(sel == 1'b0) beginout1 <= 1'b0; out2 <= 1'd0;out3 <= 1'b0; end else beginout1 <= 1'b0; out2 <= 1'd1;out3 <= 1'b1; end enddefault : beginout1 <= 1'b0; out2 <= 1'd0;out3 <= 1'b0;end endcaseend
endendmodule

VL16 占空比50%的奇数分频

讲解视频:
先看进阶版18-奇数分频(不要求占空比)

进阶版16-奇数分频(占空比50%)

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module odo_div_or (input    wire  rst ,input    wire  clk_in,output   wire  clk_out7
);//*************code***********//
reg [2:0] count_p;  //上升沿计数
reg [2:0] count_n;  //下降沿计数
reg clk_p;              //上升沿分频
reg clk_n;              //下降沿分频//上升沿计数
always @ ( posedge clk_in or negedge rst )
begin if( !rst ) count_p <= 3'b0;else if( count_p == 3'd6 ) count_p <= 3'b0;else  count_p <= count_p + 1'b1;
end//上升沿分频
always  @ ( posedge clk_in or negedge rst )
begin if( !rst ) begin clk_p <= 1'b0;end else begin if( count_p == 3'd3 || count_p == 3'd6 ) begin clk_p <= ~clk_p;endend
end//下降沿计数
always @ ( negedge clk_in or negedge rst )
begin if( !rst ) count_n <= 3'b0;else if( count_n == 3'd6 ) count_n <= 3'b0;else  count_n <= count_n + 1'b1;
end//下降沿分频
always  @ ( negedge clk_in or negedge rst )
begin if( !rst ) begin clk_n <= 1'b0;end else begin if( count_n == 3'd3 || count_n == 3'd6 ) begin clk_n <= ~clk_n;endend
end assign clk_out7 = clk_p | clk_n;endmodule

VL17 任意小数分频

先看进阶版18-奇数分频(不要求占空比)

讲解视频:进阶版17-任意小数分频

7 分频,50% 占空比。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module div_M_N(input  wire clk_in,input  wire rst,output wire clk_out
);parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期reg [6:0] cnt;
always @ (posedge clk_in or negedge rst)
beginif( ~rst ) begincnt <= 7'd0;end else beginif(cnt == 7'd86) cnt <= 7'b0;elsecnt <= cnt + 7'b1;end
end reg [2:0] cnt_8;
reg [3:0] cnt_9;
always @ (posedge clk_in or negedge rst)
beginif( ~rst ) begincnt_8 <= 3'd0;cnt_9 <= 4'd0;end else beginif(cnt < c89) begincnt_8 <= cnt_8 + 1'b1;       endelse beginif(cnt_9 == div_o - 1)cnt_9 <= 4'd0;else cnt_9 <= cnt_9 + 1'b1;     endend
endreg clk_o_r;
always @ (posedge clk_in or negedge rst)
beginif( ~rst ) beginclk_o_r <= 1'b0;end else beginif(cnt < c89) beginif(cnt_8 < 3'd4) clk_o_r <= 1'b1;elseclk_o_r <= 1'b0;       endelse beginif(cnt_9 < 3'd4) clk_o_r <= 1'b1;elseclk_o_r <= 1'b0;       endend
endassign clk_out = clk_o_r;endmodule

VL18 无占空比要求的奇数分频

讲解视频:进阶版18-奇数分频(不要求占空比)

该题目虽然说的是无占空比要求,但是实际只给了一种参考波形,只要自己写的和这个参考波形不一样就报错,所以不需要管参考波形,自己会奇数分频即可。

下方给出的代码能够通过测试,5分频,占空比40%,简单易理解。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module odd_div (    input     wire rst ,input     wire clk_in,output    wire clk_out5
);
//*************code***********//
reg [2:0] cnt;
always @ (posedge clk_in or negedge rst) beginif(~rst) begincnt <= 3'b0;endelse beginif(cnt == 3'd4) begincnt <= 3'd0;end else begincnt <= cnt + 3'd1;end end
end reg clk_out5_r;
always @ (posedge clk_in or negedge rst) beginif(~rst) beginclk_out5_r <= 3'b0;endelse begincase( cnt ) 3'd0 : clk_out5_r <= 1'b1;3'd1 : clk_out5_r <= 1'b1;3'd2 : clk_out5_r <= 1'b0;3'd3 : clk_out5_r <= 1'b0;3'd4 : clk_out5_r <= 1'b0;default : clk_out5_r <= 1'b0;endcaseend
end assign clk_out5 = clk_out5_r;endmodule

VL19 根据状态转移写状态机-三段式

讲解视频:进阶版19-三段式状态机

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module fsm1(input wire clk  ,input wire rst  ,input wire data ,output reg flag
);
//*************code***********//
parameter S0 = 4'b0001;
parameter S1 = 4'b0010;
parameter S2 = 4'b0100;
parameter S3 = 4'b1000;reg [3:0] cs, ns;
always @ (posedge clk or negedge rst)
beginif( ~rst ) begincs <= S0;end else begincs <= ns;end
end always @ (*)
begincase(cs) S0 : ns = (data == 1'b1) ? S1 : S0;S1 : ns = (data == 1'b1) ? S2 : S1;S2 : ns = (data == 1'b1) ? S3 : S2;S3 : ns = (data == 1'b1) ? S0 : S3;default : ns = S0;endcase
end always @ (posedge clk or negedge rst)
beginif( ~rst ) beginflag <= 1'b0;end else beginif( cs == S3 && data == 1'b1) beginflag <= 1'b1;end else beginflag <= 1'b0;end end
end endmodule

VL20 根据状态转移写状态机-两段式

讲解视频:进阶版20-两段式状态机

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module fsm2(input wire clk  ,input wire rst  ,input wire data ,output reg flag
);//*************code***********//
parameter S0 = 5'b00001;
parameter S1 = 5'b00010;
parameter S2 = 5'b00100;
parameter S3 = 5'b01000;
parameter S4 = 5'b10000;reg [4:0] cs, ns;
always @ (posedge clk or negedge rst)
beginif( ~rst ) begincs <= S0;end else begincs <= ns;end
end always @ (*)
begincase(cs) S0 : ns = (data == 1'b1) ? S1 : S0;S1 : ns = (data == 1'b1) ? S2 : S1;S2 : ns = (data == 1'b1) ? S3 : S2;S3 : ns = (data == 1'b1) ? S4 : S3;S4 : ns = (data == 1'b1) ? S1 : S0;default : ns = S0;endcase
end always @ (*)
beginflag = (cs == S4);
end
//assign flag = (cs == S4);endmodule

VL21 异步FIFO

先看进阶版23-二进制转格雷码

再看进阶版22-同步FIFO

同步FIFO详解

最后看进阶版21-异步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) beginif(wenc)RAM_MEM[waddr] <= wdata;
end always @(posedge rclk) beginif(renc)rdata <= RAM_MEM[raddr];
end endmodule  /********AFIFO*********/
//
// 作者:   FPGA探索者,FPGA_Explorer
//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) beginif(~wrstn) beginwaddr <= 'b0;end else beginif( winc && ~wfull ) beginwaddr <= waddr + 1'b1;end else beginwaddr <= waddr;    end end
end always @ (posedge rclk or negedge rrstn) beginif(~rrstn) beginraddr <= 'b0;end else beginif( rinc && ~rempty ) beginraddr <= raddr + 1'b1;end else beginraddr <= 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) beginif(~wrstn) beginwaddr_gray_reg  <= 'd0;endelse beginwaddr_gray_reg <= waddr_gray;end
end reg [ADDR_WIDTH:0] raddr_gray_reg;
always @ (posedge rclk or negedge rrstn) beginif(~rrstn) beginraddr_gray_reg  <= 'd0;endelse beginraddr_gray_reg <= raddr_gray;end
endreg [ADDR_WIDTH:0] addr_r2w_t;
reg [ADDR_WIDTH:0] addr_r2w;
always @ (posedge wclk or negedge wrstn) beginif(~wrstn) beginaddr_r2w_t  <= 'd0;addr_r2w    <= 'd0;endelse beginaddr_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) beginif(~rrstn) beginaddr_w2r_t  <= 'd0;addr_w2r    <= 'd0;endelse beginaddr_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);// 例化双口RAM,作者:FPGA探索者
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

VL22 同步FIFO

讲解视频:进阶版22-同步FIFO

同步FIFO详解


笔试 | 同步FIFO设计详解及代码分享(这一篇就足够~)

注意:

(1)在例化双口 RAM 的时候,读写使能 renc/wenc 信号如果只是传入 rinc 和 winc 也能通过题目的波形测试,但是仔细思考其实不对,此时如果是满的情况下有外部的写使能 winc,虽然写地址不变,但是对于 RAM 来讲写使能还是有效的,会覆盖数据,所以这里的 RAM 写使能信号 wenc = winc && (~wfull),RAM 读使能 renc = rinc && (~rempty),这样才能保证不会在空满情况下对RAM的误操作;

(2)虽然通过了波形测试,但是自己下来可以测试下空的时候继续读、满的时候继续写的极限情况,看是否出错,考虑空满信号是应该用时序逻辑产生 or 组合逻辑产生?

(3)视频里的“真双口RAM”描述是错误的,实际这里应该是简单双口 RAM,具体的去比可以参考:
Xilinx的分布式RAM和块RAM——单口、双口、简单双口、真双口的区别

`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) beginif(wenc)RAM_MEM[waddr] <= wdata;
end always @(posedge rclk) beginif(renc)rdata <= RAM_MEM[raddr];
end endmodule  /******SFIFO************/
//
// 作者:   FPGA探索者,FPGA_Explorer
//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 ADDR_WIDTH = $clog2(DEPTH);reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
always @ (posedge clk or negedge rst_n) beginif(~rst_n) beginwaddr <= 'b0;end else beginif( winc && ~wfull ) beginwaddr <= waddr + 1'b1;end else beginwaddr <= waddr;    end end
end always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginraddr <= 'b0;end else beginif( rinc && ~rempty ) beginraddr <= raddr + 1'b1;end else beginraddr <= raddr;    end end
end //assign wfull = (raddr == {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH-1:0]});
//assign rempty = (raddr == waddr);
always @ (posedge clk or negedge rst_n) beginif(~rst_n) beginwfull <= 'b0;rempty <= 'b0;end else beginwfull <= (raddr == {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH-1:0]});rempty <= (raddr == waddr);end
end // RAM例化,注意读写使能端口的信号
dual_port_RAM
#(.DEPTH(DEPTH),.WIDTH(WIDTH)
)
dual_port_RAM_U0
(.wclk(clk),.wenc(winc&&~wfull),.waddr(waddr[ADDR_WIDTH-1:0]),  //深度对2取对数,得到地址的位宽。.wdata(wdata),         //数据写入.rclk(clk),.renc(rinc&&~rempty),.raddr(raddr[ADDR_WIDTH-1:0]),  //深度对2取对数,得到地址的位宽。.rdata(rdata)        //数据输出
);    endmodule

VL23 格雷码计数器

讲解视频:进阶版23-二进制转格雷码

题目有问题,转格雷码的计数器是两个时钟才会加1。所以,考虑对二进制的计数使用5位,取高四位的时候相当于两个 clk 变化一次,然后用二进制转格雷码。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module gray_counter(input   clk,input   rst_n,output  reg [3:0] gray_out
);// 题目有问题,转格雷码的计数器是两个时钟加1
// 这样对二进制的计数使用5位,取高四位的时候相当于两个clk变化一次
reg [4:0] cnt_bin;
always @ (posedge clk or negedge rst_n)
beginif( ~rst_n ) begincnt_bin <= 5'b0;end else begincnt_bin <= cnt_bin + 5'b1;end
end wire [3:0] bin;
assign bin = cnt_bin[4:1];
always @ (gray_out, bin)
begin//gray_out = bin ^ (bin>>1);gray_out[3] = bin[3];gray_out[2] = bin[3] ^ bin[2];gray_out[1] = bin[2] ^ bin[1];gray_out[0] = bin[1] ^ bin[0];end endmodule

VL24 多bit MUX同步器

讲解视频:进阶版24-跨时钟域DMUX同步器多bit跨时钟

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module mux(input                clk_a   , input                 clk_b   ,   input               arstn   ,input              brstn   ,input      [3:0]   data_in ,input               data_en ,output reg  [3:0]     dataout
);// clk_a 时钟域的 data_en 寄存
reg data_en_a_reg;
always @ (posedge clk_a or negedge arstn)
beginif( ~arstn ) begindata_en_a_reg <= 1'b0;end else begindata_en_a_reg <= data_en;end
end// clk_a 时钟域的 data_in 寄存
reg [3:0] data_in_a_reg;
always @ (posedge clk_a or negedge arstn)
beginif( ~arstn ) begindata_in_a_reg <= 4'b0;end else begindata_in_a_reg <= data_in;end
endreg data_en_b_t;
reg data_en_b;
always @ (posedge clk_b or negedge brstn)
beginif( ~brstn ) begindata_en_b_t <= 1'b0;data_en_b <= 1'b0;end else begindata_en_b_t <= data_en_a_reg;data_en_b <= data_en_b_t;end
endalways @ (posedge clk_b or negedge brstn)
beginif( ~brstn ) begindataout <= 4'b0;end else beginif(data_en_b)dataout <= data_in_a_reg;elsedataout <= dataout;end
endendmodule

VL25 脉冲同步电路

讲解视频:进阶版25-脉冲同步跨时钟域(快时钟到慢时钟、翻转展宽)

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module pulse_detect(input               clk_fast    , input                 clk_slow    ,   input               rst_n       ,input              data_in     ,output             dataout
);reg data_in_fast;
always @ (posedge clk_fast or negedge rst_n)
beginif(~rst_n) begindata_in_fast <= 1'b0;endelse beginif(data_in == 1'b1)data_in_fast <= ~data_in_fast;else data_in_fast <= data_in_fast;end
end reg data_in_slow_t1;
reg data_in_slow_t2;
reg data_in_slow_t3;
always @ (posedge clk_slow or negedge rst_n)
beginif(~rst_n) begindata_in_slow_t1 <= 1'b0;data_in_slow_t2 <= 1'b0;data_in_slow_t3 <= 1'b0;endelse begindata_in_slow_t1 <= data_in_fast;data_in_slow_t2 <= data_in_slow_t1;data_in_slow_t3 <= data_in_slow_t2;end
endassign dataout = data_in_slow_t2 ^ data_in_slow_t3;endmodule

VL26 简易秒表

讲解视频:进阶版26-简易秒表

题目也是略有问题

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module count_module(input clk,input rst_n,output reg [5:0]second,output reg [5:0]minute
);always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginsecond <= 6'b0;minute <= 6'b0;end else beginif(minute == 6'd60) beginsecond <= second;minute <= minute;endelse if(second == 6'd60) beginsecond <= 6'b1;minute <= minute + 6'b1;endelse beginsecond <= second + 6'b1;end end
end endmodule

VL27 可置位计数器

讲解视频:进阶版27-可置位计数器

题目不是很明确。

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module count_module(input clk,input rst_n,input set,input [3:0] set_num,output reg [3:0]number,output reg zero
);reg [3:0] num_tmp;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginnum_tmp <= 4'b0;end else beginif(set) beginnum_tmp <= set_num;end else beginnum_tmp <= num_tmp + 4'b1;end end
endalways @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginnumber <= 4'b0;end else beginnumber <= num_tmp;end
end always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginzero <= 4'b0;end else beginzero <= (num_tmp == 4'b0);end
end endmodule

VL28 加减计数器

讲解视频:进阶版28-加减计数器

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module count_module(input clk,input rst_n,input mode,output reg [3:0]number,output reg zero
);reg [3:0] num_tmp;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginnum_tmp <= 4'b0;end else beginif(mode == 1'b1) beginif(num_tmp == 4'd9)num_tmp <= 4'd0;else num_tmp <= num_tmp + 4'd1;end else beginif(num_tmp == 4'd0)num_tmp <= 4'd9;elsenum_tmp <= num_tmp - 4'd1;end end
end always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginnumber <= 4'b0;end else beginnumber <= num_tmp;end
end always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginzero <= 1'b0;end else beginzero <= (num_tmp == 4'b0);end
end endmodule

VL29 单端口RAM

讲解视频:进阶版29-单端口RAM同步写入异步读出

单口RAM、简单双口RAM、真双口RAM 可以参考:
Xilinx的分布式RAM和块RAM——单口、双口、简单双口、真双口的区别

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module RAM_1port(input clk,input rst,input enb,input [6:0]addr,input [3:0]w_data,output wire [3:0]r_data
);
//*************code***********//
reg [3:0] ram_reg[127:0];
reg [3:0] ram_data;
integer i;
always @ (posedge clk or negedge rst)
beginif(~rst) beginfor(i = 0; i < 128; i = i+1) beginram_reg[i] <= 4'b0;end end else beginif(enb) begin    // writeram_reg[addr] <= w_data;end else beginram_reg[addr] <= ram_reg[addr];end end
end assign r_data = enb ? 4'b0 : ram_reg[addr];endmodule

VL30 RAM的简单实现

讲解视频:进阶版30-双口RAM的简单实现

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module ram_mod(input clk,input rst_n,   input write_en,input [7:0]write_addr,input [3:0]write_data,input read_en,input [7:0]read_addr,output reg [3:0]read_data
);reg [3:0] ram_reg[7:0];
integer i;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginfor(i = 0; i < 8; i = i+1) beginram_reg[i] <= 4'b0;end end else beginif(write_en) beginram_reg[write_addr] <= write_data;end end
end always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginread_data <= 4'b0; end else beginif(read_en) beginread_data <= ram_reg[read_addr];end end
endendmodule

VL31 Johnson Counter 约翰逊计数器

讲解视频:进阶版31-Johnson Counter【约翰逊计数器】【扭环形计数器】

FPGA/数字IC笔试题——Verilog实现 N 位 Johnson Counter【约翰逊计数器】【扭环形计数器】

解法1
`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module JC_counter(input                clk ,input                rst_n, output reg [3:0]     Q
);always @ (posedge clk,negedge rst_n)
beginif(!rst_n)Q <= {4{1'b0}};    elseQ <= {~Q[0], Q[4-1:1]};
endendmodule
解法2
`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module JC_counter(input                clk ,input                rst_n,output reg [3:0]     Q
);always @ (posedge clk,negedge rst_n)
beginif(!rst_n)Q <= {4{1'b0}};    else if(!Q[0])Q <= {1'b1,Q[4-1:1]};elseQ <= {1'b0,Q[4-1:1]};
endendmodule

VL32 流水线乘法器

讲解视频:进阶版32-流水线乘法器

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module multi_pipe#(parameter size = 4
)(input                         clk         ,   input                       rst_n       ,input  [size-1:0]          mul_a       ,input  [size-1:0]          mul_b       ,output reg [size*2-1:0]    mul_out
);reg [size*2-1:0] mul_a_t [size-1:0];
integer i;
integer j;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginfor(i = 0; i < size; i = i+1) beginmul_a_t[i] <= 'b0;end end else beginfor(j = 0; j < size; j = j+1) beginmul_a_t[j] <= mul_b[j] ? mul_a << j : 'b0;endend
end
reg [size*2-1:0] sum_t[size-1:0];
integer n;
always @ (*)
beginfor(n = 0; n < size; n = n+1) beginif(n == 0) sum_t[0] = mul_a_t[0];elsesum_t[n] = sum_t[n-1] + mul_a_t[n];end
end                                           always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginmul_out <= 'b0;end else beginmul_out <= sum_t[size-1];end
end
endmodule

VL33 交通灯

讲解视频:进阶版33-Verilog交通灯

题目描述很不明确,根据多次测试波形,得到以下注意点:

(1)复位状态:red/yellow/green 均为 0,倒计时数 clock = 10;

(2)复位以后,有 2 个时钟的复位释放期,在此期间,red/yellow/green 均为 0,倒计时 clock = 9、8;2 个周期以后,red = 1,yellow/green 均为 0,倒计时 clock = 10,从此开始进行红灯倒计时;

(3)交通灯颜色变化顺序:红、黄、绿;

(4)倒计时范围:红灯 10、9、…、1;黄灯 5、4、…、1;绿灯 60、59、…、1;

(5)当有行人按钮按下,倒计时立刻变化(考虑时序逻辑 or 组合逻辑);

代码
`timescale 1ns / 1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module triffic_light (input rst_n, //异位复位信号,低电平有效input clk, //时钟信号input pass_request,output wire[7:0]clock,output reg red,output reg yellow,output reg green
);// 1. 使用 3 个计数器,分别计数红、黄、绿
// 2. 使用 1 个计数器,根据计数值的范围分别表示红、黄、绿
// 0~9:  红色 10
// 10~14:黄色 5
// 15~74:绿色 60reg [6:0] count75;
reg flag;
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) begincount75 <= 7'b0;flag <= 1'b0;end else beginif(pass_request) beginif(count75 <= 7'd65)count75 <= 7'd65;elsecount75 <= count75 + 7'b1;end else beginif(flag == 1'b0) beginif(count75 == 7'd2) beginflag <= 1'b1;count75 <= 7'b0;end else begincount75 <= count75 + 7'b1;end end else beginif(count75 == 7'd74)count75 <= 7'b0;elsecount75 <= count75 + 7'b1;endend end
end // 红、黄、绿
always @ (posedge clk or negedge rst_n)
beginif(~rst_n) beginred <= 1'b0;yellow <= 1'b0;green <= 1'b0; end else beginif(count75 >=0 && count75 < 9 || count75 == 7'd74) beginif(flag == 1'b0 && count75 != 7'd2)red <= 1'b0;elsered <= 1'b1;yellow <= 1'b0;green <= 1'b0;end else if(count75 >=9 && count75 < 14) beginred <= 1'b0;yellow <= 1'b1;green <= 1'b0; endelse if(count75 >=14 && count75 < 74) beginred <= 1'b0;yellow <= 1'b0;green <= 1'b1;end else beginred <= red;yellow <= yellow;green <= green;end end
end // 倒计时
reg [7:0] clock_t;
always @ (rst_n, pass_request, count75, flag) beginif(~rst_n) begin          clock_t = 8'd10;end else beginif(count75 >=0 && count75 <= 9) beginclock_t = 8'd10 - count75;end else if(count75 >=10 && count75 <= 14) beginclock_t = 8'd15 - count75;endelse if(count75 >=15 && count75 <= 74) beginclock_t = 8'd75 - count75;end else beginclock_t = clock_t;end end
end assign clock = clock_t;endmodule
TestBench
`timescale 1ns / 1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//module triffic_light_tb();reg rst_n; //异位复位信号,低电平有效
reg clk; //时钟信号
reg pass_request;
wire [7:0]clock;
wire red;
wire yellow;
wire green;initial beginclk = 0;rst_n = 0;pass_request = 0;#15;rst_n = 1;#1000;pass_request = 1;#10;pass_request = 0;#500;$stop;
end always #5 clk = ~clk;triffic_light triffic_light_U0
(.rst_n(rst_n), //异位复位信号,低电平有效.clk(clk), //时钟信号.pass_request(pass_request),.clock(clock),.red(red),.yellow(yellow),.green(green)
);endmodule

VL34 游戏机计费程序

讲解视频:进阶版34-游戏机计费程序

`timescale 1ns/1ns
//
// 作者:   FPGA探索者,FPGA_Explorer
//
module game_count (input rst_n, //异位复位信号,低电平有效input clk,     //时钟信号input [9:0]money,input set,input boost,output reg[9:0]remain,output reg yellow,output reg red
);always@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0) beginremain <= 10'd0;end else beginif(set == 1'b1)remain <= remain + money;else if(boost == 1'b1) beginif(remain <= 10'd2)remain <= 10'd0;elseremain <= remain - 10'd2;end else if(boost == 1'b0) beginif(remain <= 10'd1)remain <= 10'd0;elseremain <= remain - 10'd1;end else remain <= remain;end
endalways@(posedge clk or negedge rst_n)
beginif(rst_n == 1'b0)yellow <= 1'b0;else beginif((remain > 10'd0) && (remain < 10'd10))yellow <= 1'b1;else yellow <= 1'b0;end
endalways @ (posedge clk or negedge rst_n)
beginif(rst_n == 1'b0) beginred <= 1'b0;end else beginif(remain < 10'd1)red <= 1'b1;elsered <= 1'b0;end
endendmodule

视频讲解合集:

FPGA数字IC刷题Verilog讲解
https://space.bilibili.com/507257163/channel/collectiondetail?sid=260755

刷题链接点击转到
https://www.nowcoder.com/exam/oj?tab=Verilog%E7%AF%87&topicId=302&fromPut=pc_zh_s_1540795715

结束

FPGA数字IC刷题58道Verilog题解代码及视频讲解【FPGA探索者】【同步/异步FIFO】【跨时钟】相关推荐

  1. 2022届FPGA/数字IC秋招笔试面试汇总帖(题目来源:FPGA探索者)

    目录 001--什么是STA静态时序分析,有什么作用? 具有代表性的STA工具: 静态时序分析STA: (1)setup time (2)hold time (3)STA 的时序路径 (4)recov ...

  2. FPGA/数字IC实用笔试面试刷题汇总

    (1)HDLBits:Verilog基础题比较多,题量大,波形对比比较好用,全部是Verilog编程,适合初学者锻炼代码编程能力(题目全是英文版的): https://hdlbits.01xz.net ...

  3. 艾为数字ic面试题_秋招 应聘FPGA/数字IC笔试面试经验分享(简单列举FPGA/数字IC公司)...

    1.自我介绍 我是一名二本院校的电气自动化专业的本科生,因为自己的爱好,喜欢FPGA开发,两年的开发经验,用FPGA开发并完成三个省级科研立项并获得一个A类竞赛国家一等奖.目前签约上海艾为电子,数字I ...

  4. FPGA/数字IC手撕代码8——秒表计数器

    深度学习/机器视觉/数字IC/FPGA/算法手撕代码目录总汇 目录 秒表计数器 1.程序 2.测试 3.仿真结果 4.分析 秒表计数器 1.程序 `timescale 1ns/1nsm

  5. LeetCode按知识点刷题,额外附带题解

    刷题顺序转自:ACM金牌选手整理的[LeetCode刷题顺序]_清澈不在远方-CSDN博客 题解为本人自刷 数据结构 数组&双指针 LeetCode 1. 两数之和 /*** 给定一个整数数组 ...

  6. 【HDLBits 刷题 4】Verilog Language(4)Procedures 和 More Verilog Features 部分

    目录 写在前面 Procedures Alwaysblock1 Alwaysblock2 Always if Always if2 Always case Always case2 Always ca ...

  7. 【FPGA/数字IC】加法器总结

    半加器 半加器的输入为两个1bit信号A,B,输出为它们的和S以及进位标志位C 其真值表如下表所示 A B S C 0 0 0 0 0 1 1 0 1 0 1 0 1 1 0 1 由此可得其逻辑表达式 ...

  8. leetcode-374. 猜数字大小刷题笔记(c++)

    写在前面 常规.简单题目 二分法高效搜索目标值 题目详情 我们正在玩一个猜数字游戏. 游戏规则如下: 我从 1 到 n 选择一个数字. 你需要猜我选择了哪个数字. 每次你猜错了,我会告诉你这个数字是大 ...

  9. 刷题百道却跪在白板面试?白板面试FAQ为你答疑解惑

    专栏 | 九章算法 网址 | www.jiuzhang.com 你花了大量的时间学习如何编程.学习如何创建项目.终于你通过自己的努力收到了梦寐以求的公司发来的面试邀请函.但是这时候,你却发现你的面前又 ...

最新文章

  1. 监控工具之zabbix server3.4 部署配置
  2. 雅加达EE:干净的板岩
  3. apache camel_Apache Camel入门
  4. OpenCv色彩通道分离与融合
  5. i++与++i的区别+汇编分析
  6. vue实现图片预加载的几种方式
  7. 创建Mac的shell命令文件(xxx.sh)
  8. 03矩阵的乘法与逆矩阵
  9. Spring Framework系统架构
  10. Unity3D摄像机Camera参数详解
  11. Connection refused:connect@src/mongo/shell/mongo.js:374:17 @(connect):2:6 exception: connect failed
  12. stm32使用PWM时,关闭PWM引脚会出现高电平解决方案
  13. Raccoon——(Misc)Black Night writeup
  14. springboot在线原型工程生成工具
  15. da转换器正弦波c语言,AD与DA转换器和系统的连接及应用
  16. Python邮箱提醒
  17. 使用 STM32 ST-LINK Utility解除芯片的读保护
  18. 惊呆了,看华为技术专家的500页微服务架构笔记,感觉我格局太小
  19. These dependencies were not found解决办法
  20. Gym 101889E、Enigma(DFS)

热门文章

  1. 解决npm一直停在“checking installable status“的问题
  2. 练习3-7 成绩转换(15 分)
  3. Github 配置SSH key 下载项目并关联
  4. 壬辰年癸丑月丁亥日记梦
  5. Newtonsoft.Json.dll使用
  6. J.P. Morgan:AI for Investing(脱水解读)
  7. Android培训班(62)dex文件格式3
  8. 2016年最火最牛的内存漏洞分析-dirtycow
  9. 空字符串和null有什么区别
  10. Nginx:正向代理与反向代理