Verilog 学习之路

7 篇文章 0 订阅
2 篇文章 0 订阅
本文详细解释了SystemVerilog中的generate构造和for循环在创建硬件结构中的应用,以及always块在硬件描述中的作用,包括组合逻辑和时序逻辑的实现。同时,讨论了非阻塞赋值(<=)和阻塞赋值(=)的区别,并举例说明了$bits函数的用途和计数器的实现。
摘要由CSDN通过智能技术生成

循环 7-10

  • 代码段
generate
		genvar i;
		for (i=0; i<8; i = i+1) begin: my_block_name
			assign out[i] = in[8-i-1];
		end
endgenerate
  • 解释

该代码使用了 S y s t e m V e r i l o g SystemVerilog SystemVerilog 中的 g e n e r a t e generate generate 构造,它允许在编译期间创建硬件结构。 g e n v a r genvar genvar 声明定义了一个生成变量 $ i$,可以用于索引。

g e n e r a t e generate generate 块内部, f o r for for 循环从0到7进行迭代( i i i < 8 8 8)。对于每次迭代, a s s i g n assign assign 语句将 i n [ 8 − i − 1 ] in[8 - i - 1] in[8i1] 的值赋给 o u t [ i ] out[i] out[i]。这将颠倒 i n in in 数组中的元素顺序,并将它们赋值给 o u t out out 数组中对应的元素。

请注意, g e n e r a t e generate generate 构造在硬件描述语言(如 S y s t e m V e r i l o g SystemVerilog SystemVerilog)中用于创建硬件结构,但它不是常规过程执行流程的一部分。因此,该代码必须放置在支持 g e n e r a t e generate generate构造的模块或其他适当的上下文中。

  • 代码段
always @(*) begin	
		for (int i=0; i<8; i++)	// int is a SystemVerilog type. Use integer for pure Verilog.
			out[i] = in[8-i-1];
end
  • 解释

ChatGPT解释

always 块 7.14

对于硬件合成,有两类始终块是相关的:

组合式:always @(*)
时钟式:always @(posedge clk)
组合总是块等同于赋值语句,因此总有一种方法可以用两种方式来表达组合电路。选择哪种方式主要是看哪种语法更方便。程序块内部代码的语法与外部代码的语法不同。过程块有更丰富的语句集(如if-then、case),不能包含连续赋值*,但也引入了许多新的非直观的错误方式。(*程序连续赋值确实存在,但与连续赋值有些不同,而且不可合成)。

例如,(the assign and combinational always block )分配和组合总是块描述相同的电路。两者都创建了相同的组合逻辑。每当任何输入(右侧)的值发生变化时,两者都将重新计算输出。
在组合逻辑always块中使用敏感性列表的最佳实践。建议使用()作为敏感性列表,而不是显式列出所有信号,因为这样容易出错,并且在硬件综合过程中会被忽略。如果您显式指定了敏感性列表并漏掉了一个信号,则合成的硬件仍将像()一样运行,但仿真不会匹配硬件的行为。(在SystemVerilog中,使用always_comb。)
Verilog中使用的两种不同的变量类型:wire和reg。

在Verilog中,wire类型用于表示连线或信号,它在描述硬件电路时用于连接不同的模块或元件。它通常用于assign语句中,用于在连续赋值中连接模块的输入和输出。

而reg类型则用于表示寄存器或存储器元素,它在描述硬件电路时用于存储数据或状态。它通常用于always块中的过程赋值语句,用于描述时序逻辑或状态机。

这两种类型的选择与硬件综合无关,只是Verilog作为硬件仿真语言时的语法约定。在实际综合成硬件时,这些变量类型都会被映射到适当的硬件元素。
<= 和 = 的区别
在 Verilog 中,<= 和 = 是赋值操作符,但在 always 块内的上下文中,它们具有不同的含义和行为。

<= 赋值操作符:

<= 被称为非阻塞赋值(non-blocking assignment)操作符。
在 always 块内部使用 <= 进行赋值时,该赋值是并发进行的。
当 always 块的敏感信号(敏感列表)发生变化时,所有的 <= 赋值同时生效,以并行的方式更新信号的值。
这种并发赋值模型可以用于描述多个并行操作,例如时序逻辑中的寄存器更新或多个线程之间的通信。
= 赋值操作符:

= 被称为阻塞赋值(blocking assignment)操作符。
在 always 块内部使用 = 进行赋值时,该赋值是顺序进行的。
当 always 块的执行到达 = 赋值时,它会阻塞(暂停)之后的语句,直到完成当前的赋值操作,然后再继续执行下一个语句。
这种顺序赋值模型可以用于描述时序逻辑中的组合逻辑操作,其中后续的语句可能依赖于当前赋值的结果。
在常见的用法中,通常在组合逻辑中使用 = 赋值操作符,而在时序逻辑(如 always @(posedge clk))中使用 <= 非阻塞赋值操作符。这样可以确保在时序逻辑中的多个并发赋值在时钟沿上并行执行,而在组合逻辑中的连续赋值按顺序进行。

$bits(in) 7.18

在Verilog中,$bits(in)是一种用于获取信号或变量的位宽的系统函数。它返回一个整数,表示给定信号或变量的位宽。

100位的全加器

module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );
    always @(*) begin
        {cout[0],sum[0]} = a[0] + b[0] + cin;
        for (int i = 1;i < $bits(a);i ++)
        {cout[i],sum[i]} = a[i] + b[i] + cout[i-1];
    end
endmodule

BCD加法器

实例化100份bcd_fadd,创建一个100位的BCD纹波携带加法器。您的加法器应将两个100位BCD数(打包成400位向量)相加,再加上一个进位,以产生一个100位数的和并进行输出。

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );

    wire [400:0] midcout;

    assign cout = midcout[400];
    
    bcd_fadd bcd_fadd_0(.a(a[3:0]), .b(b[3:0]), .cin(cin), .cout(midcout[4]), .sum(sum[3:0]));

    generate//子模块不可在always模块内部调用,可以生成模块重复调用
        genvar i;
        for(i=4;i<$bits(a);i=i+4)
            begin:Go
                bcd_fadd bcd_fadd_i(.a(a[i+3:i]), .b(b[i+3:i]), .cin(midcout[i]), .cout(midcout[i+4]), .sum(sum[i+3:i]));
            end
    endgenerate
    
endmodule

部分切片 7.19

assign out = in[sel * 4 +: 4];

在这里插入图片描述

半加器

module top_module( 
    input a, b,
    output cout, sum );
    assign {cout,sum} = a+b;
endmodule

在这里插入图片描述

检测输入上边沿 7.20

之所以用 & 逻辑,是因为当in == 1,~last == 1,这样就是1了。

always @(posedge clk) begin
		d_last <= in;			// Remember the state of the previous cycle
		pedge <= in & ~d_last;	// A positive edge occurred if input was 0 and is now 1.
	end

Dualedge

module top_module(
	input clk,
	input d,
	output q);
	
	reg p, n;
	
	// A positive-edge triggered flip-flop
    always @(posedge clk)
        p <= d ^ n;
        
    // A negative-edge triggered flip-flop
    always @(negedge clk)
        n <= d ^ p;
    
    // Why does this work? 
    // After posedge clk, p changes to d^n. Thus q = (p^n) = (d^n^n) = d.
    // After negedge clk, n changes to d^p. Thus q = (p^n) = (p^d^p) = d.
    // At each (positive or negative) clock edge, p and n FFs alternately
    // load a value that will cancel out the other and cause the new value of d to remain.
    assign q = p ^ n;
    
    
	// Can't synthesize this.
	/*always @(posedge clk, negedge clk) begin
		q <= d;
	end*/
    
    
endmodule

计数器

module top_module (
    input clk,
    input reset,      // Synchronous active-high reset
    output [3:0] q);
    always @(posedge clk) begin
        if (reset)
            q <= 4'd0;
        else 
            q <= q + 1'd1;
    end
endmodule

1-12 计数器——还不会

在这里插入图片描述
在这里插入图片描述

module top_module (
    input clk,
    input reset,
    input enable,
    output [3:0] Q,
    output c_enable,
    output c_load,
    output [3:0] c_d
); //
    
    assign c_enable = enable;
    assign c_load = reset | ((Q == 4'd12) && enable == 1'b1);
    assign c_d = c_load ? 4'd1 : 4'd0;
 
    count4 u_counter (clk, c_enable, c_load, c_d, Q);
 
endmodule

计数器1000

从 1000 Hz 时钟中导出一个 1 Hz 信号(称为 OneHertz),该信号可用于驱动一组时/分/秒计数器的使能信号,从而创建一个数字挂钟。由于我们希望时钟每秒计数一次,因此 OneHertz 信号必须在每秒的一个周期内准确断言。使用模数转换 10(BCD)计数器和尽可能少的其他门来构建分频器。同时输出每个 BCD 计数器的使能信号(最快的计数器输出 c_enable[0],最慢的计数器输出 c_enable[2])。
在这里插入图片描述

module top_module (
    input clk,
    input reset,
    output OneHertz,
    output [2:0] c_enable
); //
    reg [3:0] Q0,Q1,Q2;
    always @(posedge clk) begin
        if (reset)
            c_enable[0] = 1;
    end
    assign c_enable[1] = (Q0 == 4'd9);
    assign c_enable[2] = ({Q1,Q0} == 8'h99);
    assign OneHertz = ({Q2,Q1,Q0} == 12'h999)? 1'b1:1'b0;
    bcdcount counter0 (clk, reset, c_enable[0],Q0/*, ... */);
    bcdcount counter1 (clk, reset, c_enable[1],Q1/*, ... */);
    bcdcount counter2 (clk, reset, c_enable[2],Q2/*, ... */);

endmodule

12-hour时钟

module top_module(
    input clk,
    input reset,
    input ena,
    output pm,
    output [7:0] hh,
    output [7:0] mm,
    output [7:0] ss); 
    
    reg 		pm_temp;
    reg	[3:0]	ss_ones;
    reg [3:0]	ss_tens;
    reg	[3:0]	mm_ones;
    reg [3:0]	mm_tens;
    reg	[3:0]	hh_ones;
    reg [3:0]	hh_tens;
    wire		add_ss_ones;
    wire		end_ss_ones;
    wire		add_ss_tens;
    wire		end_ss_tens;
    wire		add_mm_ones;
    wire		end_mm_ones;
    wire		add_mm_tens;
    wire		end_mm_tens;
    wire		add_hh_ones;
    wire		end_hh_ones_0;
    wire		end_hh_ones_1;
    wire		add_hh_tens;
    wire		end_hh_tens_0;
    wire		end_hh_tens_1;
    wire		pm_ding;
    
    always@(posedge clk)begin
        if(reset)begin
            ss_ones <= 4'd0;
        end
        else if(add_ss_ones)begin
            if(end_ss_ones)begin
                ss_ones <= 4'd0;
            end
            else begin
                ss_ones <= ss_ones + 1'b1;
            end
        end
    end
    
    assign add_ss_ones = ena;
    assign end_ss_ones = add_ss_ones && ss_ones == 4'd9;
    
    always@(posedge clk)begin
        if(reset)begin
            ss_tens <= 4'd0;
        end
        else if(add_ss_tens)begin
            if(end_ss_tens)begin
                ss_tens <= 4'd0;
            end
            else begin
                ss_tens <= ss_tens + 1'b1;
            end
        end
    end
    
    assign add_ss_tens = end_ss_ones;
    assign end_ss_tens = add_ss_tens && ss_tens == 4'd5;
    
    always@(posedge clk)begin
        if(reset)begin
            mm_ones <= 4'd0;
        end
        else if(add_mm_ones)begin
            if(end_mm_ones)begin
                mm_ones <= 4'd0;
            end
            else begin
                mm_ones <= mm_ones + 1'b1;
            end
        end
    end
    
    assign add_mm_ones = end_ss_tens;
    assign end_mm_ones = add_mm_ones && mm_ones == 4'd9;
    
    always@(posedge clk)begin
        if(reset)begin
            mm_tens <= 4'd0;
        end
        else if(add_mm_tens)begin
            if(end_mm_tens)begin
                mm_tens <= 4'd0;
            end
            else begin
                mm_tens <= mm_tens + 1'b1;
            end
        end
    end
    
    assign add_mm_tens = end_mm_ones;
    assign end_mm_tens = add_mm_tens && mm_tens == 4'd5;
    
    always@(posedge clk)begin
        if(reset)begin
            hh_ones <= 4'd2;
        end
        else if(add_hh_ones)begin
            if(end_hh_ones_0)begin
                hh_ones <= 4'd0;
            end
            else if(end_hh_ones_1)begin
                hh_ones <= 4'd1;
            end
            else begin
                hh_ones <= hh_ones + 1'b1;
            end
        end
    end
    
    assign add_hh_ones = end_mm_tens;
    assign end_hh_ones_0 = add_hh_ones && hh_ones == 4'd9;
    assign end_hh_ones_1 = add_hh_ones && (hh_tens == 4'd1 && hh_ones == 4'd2);
    
    always@(posedge clk)begin
        if(reset)begin
            hh_tens <= 4'd1;
        end
        else if(add_hh_tens)begin
            if(end_hh_tens_0)begin
                hh_tens <= 4'd0;
            end
            else if(end_hh_tens_1)begin
                hh_tens <= hh_tens + 1'b1;
            end
        end
    end
    
    assign add_hh_tens = end_mm_tens;
    assign end_hh_tens_0 = add_hh_tens && end_hh_ones_1;
    assign end_hh_tens_1 = add_hh_tens && end_hh_ones_0;
    
    always@(posedge clk)begin
        if(reset)begin
            pm_temp <= 1'b0;
        end
        else if(pm_ding)begin
            pm_temp <= ~pm_temp;
        end
    end
    
    assign pm_ding = hh_tens == 4'd1 && hh_ones == 4'd1 && end_mm_tens;
    
    assign ss = {ss_tens, ss_ones};
    assign mm = {mm_tens, mm_ones};
    assign hh = {hh_tens, hh_ones};
    assign pm = pm_temp;
 
endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂的码泰君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值