11

Verilog小总结

 3 years ago
source link: http://www.cnblogs.com/BUAA-YiFei/p/13939329.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Verilog小总结

基础

assign

assign作为一个组合逻辑常用的语句,可认为是将电线连接起来,当然它能做的不仅仅是将一个输入直接输出,它能把输入信号进行逻辑运算后再输出。 当assign左右两边位宽不相等时,将自动进行零扩展或截断以匹配左边的位宽。

eg: mmIFFnE.png!mobile

module top_module (
	input a,
	input b,
	input c,
	input d,
	output out,
	output out_n );
	wire w1, w2;		// Declare two wires (named w1 and w2)
	assign w1 = a&b;	// First AND gate
	assign w2 = c&d;	// Second AND gate
	assign out = w1|w2;	// OR gate: Feeds both 'out' and the NOT gate
	assign out_n = ~out;	// NOT gate
endmodule

Vectors

声明向量

type [upper:lower] vector_name;
type 指定向量的数据类型,通常是 wirereg 。如果要声明输入或输出端口,则该类型还可以另外包括端口类型(例如, inputoutput

wire [7:0] w;         // 8-bit wire
reg  [4:1] x;         // 4-bit reg
output reg [0:0] y;   // 1-bit reg that is also an output port (this is still a vector)
input wire [3:-2] z;  // 6-bit wire input (negative ranges are allowed)
output [3:0] a;       // 4-bit output wire. Type is 'wire' unless specified otherwise.
wire [0:7] b;         // 8-bit wire where b[0] is the most-significant bit.

部分选择

使用向量名称访问整个向量,但是当assign左右两边位宽不相等时,将自动进行零扩展或截断以匹配左边的位宽。

使用 vector_name[up:low] 的形式获取部分向量,注意方向应与定义的一致,如定义了一个 a[3:0] ,那么不能反向获取 a[0:3]

矢量运算

  • 位运算
符号 功能 ~ 按位取反 & 按位与 | 按位或 ^ 按位异或 ^~ 按位同或

注意:除了 ~ 外均为双目运算符;若进行双目运算时左右两个操作数位数不一样,位数少的将在相应的高位用0扩展。

  • 逻辑运算

逻辑运算会将整个向量视为布尔值(真=非零,假=零),并且产生1位输出,如有 input [2:0] ainput [2:0] b 那么他们的逻辑或运算即为 assign out = a || b; ,a和b均视为一个布尔值。

  • 缩减运算

对一个向量的每一位进行位操作,如有 a[2:0] ,那么 b=&a 相当于 b=(a[0]&a[1])&a[2]

矢量串联

串联运算符 {a,b,c} 用来将小向量串联起来创建一个更大的向量。 串联中不允许使用不定尺寸的常量。{1,2,3} 是非法的,因为Verilog不知道他们的位宽。

还可以用 {n{vec}} 的形式来复制向量,如 {6{a}}{a,a,a,a,a,a} 是一样的,同时注意两组大括号都是必须的,即 {1'b1,6{1'b0}} 是非法的,因为其中的 6{1'b0} 少了一组大括号,正确的写法是 {1'b1,{6{1'b0}}} 。这其实比较好理解,串联运算符 {a,b,c} 中的abc均为一个向量, {n{vec}} 也代表了一个向量,因此 {a,b,{n{c}}} 也是一个向量

模块

mod_name instance_name (signal_name1,signal_name2,signal_name3);//by position

mod_name instance_name (.port_name1(signal_name1),.port_name2(signal_name2),.port_name3(signal_name3));//by name

可以理解为一个函数,注意括号内的是外部连接到模块的信号。

always块

组合逻辑

使用 always @(*) 可以类似于 assign 的效果,当右方有变量发生改变时,左边输出随之立即改变。 assign out1 = a & b | c ^ d;always @(*) out2 = a & b | c ^ d; 是一样的

时序逻辑

  • 同步与异步复位
//同步复位
always @(posedge clk) begin
    if(reset == 1) begin
        //reset
    end
end
//异步复位
always @(posedge clk,posedge areset) begin
    if(areset == 1) begin
        //reset
    end
end
  • 阻塞赋值非阻塞赋值

一般来说,我们在组合逻辑的always块中使用阻塞赋值( x = y; );在时序逻辑的always块中使用非阻塞赋值( x <= y;

case

always @(*) begin     //这是一个组合逻辑
    case (in)
      1'b1: begin 
               out = 1'b1;  
            end
      1'b0: out = 1'b0;
      default: out = 1'bx;
    endcase //一定记得写endcase
end

注意一定要写endcase。

另外还有case的好兄弟casez,他可以匹配形如 4'bzzz1 的向量,z表示无关位。

eg: 优先编码器

module top_module (
    input [7:0] in,
    output reg [2:0] pos  );
    always @(*) begin
        casez (in)
            8'bzzzzzzz1 : pos = 0;
            8'bzzzzzz1z : pos = 1;
            8'bzzzzz1zz : pos = 2;
            8'bzzzz1zzz : pos = 3;
            8'bzzz1zzzz : pos = 4;
            8'bzz1zzzzz : pos = 5;
            8'bz1zzzzzz : pos = 6;
            8'b1zzzzzzz : pos = 7;
            default: pos =0;
        endcase
    end   
endmodule

for

组合for循环

与C语言的用法类似。

eg: 人口计数器

module top_module (
	input [254:0] in,
	output reg [7:0] out
);
    always @(*) begin	//组合逻辑always块
		out = 0;        //一定要初始化为0
		for (int i=0;i<255;i++)
			out = out + in[i];
	end
endmodule

生成for循环

当对矢量中多个位进行重复操作时,或进行多个模块的实例化引用的重复操作时,可使用生成块简化程序。写法如下

genvar i;//只能用genvar作为循环变量
    generate
        for (i=1;i<99;i=i+1) begin: add_loop//这个名字是必须的
            mod_name instance_name(......);//括号里写由i推出的信号
       	end
    endgenerate

eg: Bcdadd100

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );
    genvar i;
    wire [99:0]cout1;
    bcd_fadd mod1(a[3:0],b[3:0],cin,cout1[0],sum[3:0]);
    generate
        for (i=1;i<99;i=i+1) begin: addloop
            bcd_fadd mod2(a[(4*i+3):(4*i)],b[(4*i+3):(4*i)],cout1[i-1],cout1[i],sum[(4*i+3):(4*i)]);
        end
    endgenerate
    bcd_fadd mod3(a[399:396],b[399:396],cout1[98],cout,sum[399:396]);
endmodule

状态机写法

Moore型

三段式写法:使用一个state用于存当前状态,使用一个next_state用于存下一状态。第一段用于写状态转换逻辑,第二段用于状态转移,第三段用于输出。

reg state, next_state;

	//第一段:
	always @(*) begin    //一个组合逻辑always块,用于写状态转换逻辑,当in改变时,next_state将立即改变。
        case(state)
            A: next_state = f(in)//关于in的函数
            B: next_state = f(in)
            ...    
        endcase
    end
                
	//第二段(异步):
    always @(posedge clk, posedge areset) begin    
        if(areset == 1) begin
            state <= 0;//reset
        end
        else state <= next_state;
    end
	//第二段(同步):
    always @(posedge clk) begin   
        if(reset == 1) begin
            state <= 0;//reset
        end
        else state <= next_state;
    end
            
    //第三段(assign法)
            assign out = (state == ...);//判断state
	//第三段(组合逻辑always块法)
    always@(*) begin
        case (state)
            A: {out3,out2,out1} = 3'b111;
            B: {out3,out2,out1} = 3'b110;//对每一种状态输出
            ...
        endcase
    end

Mealy型

仅仅第三段发生了改变,可使用 {state,in} 来做输出判断。

//第三段(assign法)
    assign out = f(state,in);//关于state和in的函数
    //第三段(组合逻辑always块法)
    always@(*) begin
        case ({state,in})
            4'b0000: {out3,out2,out1} = 3'b111;
            4'b0001: {out3,out2,out1} = 3'b110;//对每一种state与in做输出
            ...
    end

(希望明天P1能过呜呜呜


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK