Verilog基础模块总结

verilog基础模块

verilog基础模块包括数据类型,运算符,组合逻辑和时序逻辑四个部分。数据类型包括常量和变量,在常量中有整数,X和Z以及参数。X代表不定制,Z代表高阻值。下划线不具有任何意义,是用来分割位数,提高程序可读性,8‘b0000_如1111。常量中的参数parameter可以用标识符定义常量,运用时只使用标识符即可,提高程序可读性及维护性,如定义parameter width=8;定义寄存器reg[width-1:0]a;即定义了8位宽度的寄存器。

参数传递:在一个模块中如果有定义参数,在其他模块调用此模块时可以传递参数,并可以修改参数,如下所示,在module后用#()表示。

module rom
#(
    parameter depth=15,
    parameter width=8
 )
 (
    input [depth-1:0]addr,
    input [width-1:0]data,
    output result
);
endmodule

module top();
wire[31:0] addr;
wire[15:0] data;
wire result;

rom
#(
    .depth(32),
    .witdth(16)
 )
r1
(
    .addr(addr),
    .data(data),
    .result(result)
);
endmodule

parameter可以用于模块间的参数传递,而localparam仅用于本模块内使用,不能用于参数传递。localparam多用于状态机状态的定义

数据类型中的变量有Wire型,Reg型,Memory型。wire类型变量,也叫网络类型变量,用于结构实体之间的物理连接,如门与门之间,不能存储值,用连续赋值语句assign赋值,定义为wire[n-1:0]a;其中n代表位宽,如定义wire;assign a=b;是将b的节点连接到连线a上。Reg类型变量,也称为寄存器变量,可用来储存值,必须在always语句里使用(值变化需时钟)。 其定义为reg[n-1:0]a;表示n位位宽的寄存器,如reg[7:0]a;表示定义8位位宽的寄存器a。也可以生成组合逻辑,如数据选择器,敏感信号没有时钟。可以用memory类型来定义RAM,ROM等存储器,其结构为reg[n-1:0]存储器名[m-1:0],表示为m个n位深度的寄存器。

运算符包括(1)算术运算符(+,-,*,/,%);(2)赋值运算符(=,<=);(3)关系运算符(>,<,>=,<=,==,!=);(4)逻辑运算符(&&,||,!);(5)条件运算符(?:);(6)位运算符(~,|,^,&,^~);(7)移位运算符(<<,>>);(8)拼接运算符({})。

这里面着重探讨一下赋值运算符“=”和“<=”。“=”为阻塞赋值,“<=”非阻塞赋值。阻塞赋值为执行完一条赋值语句,再执行下一条,可理解为顺序执行,而且赋值是立即执行非阻塞赋值可理解为并行执行,不考虑顺序,在always块语句执行完成后,才进行赋值。一般情况下,在时序逻辑电路中使用非阻塞赋值可避免仿真时出现竞争冒险现象在组合逻辑中使用阻塞赋值,执行赋值语句后立即改变在assign语句中必须用阻塞赋值

组合逻辑电路的特点时任意时刻的输出仅仅取决于输入信号,输入信号变化,输出立即变化,不依赖于时钟。如与门“&”;或门“|”;非门“~”;异或门“^”;比较器;半加器(半加器和全加器时算术运算电路中的基本单元,由于半加器不考虑从低位来的进位,所以称之为半加器);全加器;乘法器;数据选择器(通过选择信号,选择不同的输入信号输出到输出端);3-8译码器;三态门。

时序逻辑电路在逻辑功能上特点是任意时刻的输出仅仅取决于当前时刻的输入与电路原来的状态无关。而时序逻辑在逻辑功能上的特点是任意时刻的输出不仅仅取决于当前的输入信号,而且取决于电路原来的状态。如D触发器(D触发器在时钟的上升沿或下降沿存储数据,输出与时钟跳变之前输入信号的状态相同。)两级D触发器,带异步复位的D触发器(异步复位是指独立于时钟,一旦复位信号有效,就触发复位操作。);异步复位同步清零的D触发器;移位寄存器(移位寄存器是指在每个时钟脉冲来时,向左或向右移动一位,由于D触发器的特性,数据输出同步于时钟边沿,每个时钟来临,每个D触发器的输出q等于前一个D触发器输出的值,从而实现移位的功能)。

然后是几位重磅级的时序逻辑电路:

1.单口RAM:单口RAM的写地址与读地址公用一个地址,代码如下,其中reg[7:0]ram[63:0]意思是定义了64个8位宽度的数据。其中定义了addr_reg,可以保持住读地址,延迟一周期之后将数据送出。

module top
(
    input[7:0]data,
    input[5:0]addr,
    input wr,
    input clk,
    output[7:0] q
);
reg[7:0] ram[63:0]; //declare ram
reg[5:0] addr_reg;  //addr register

always @(posedge clk)
begin
    if(wr)
        ram[addr]<=data;
        addr_reg<=addr;
end
assign q=ram[addr_reg];
endmodule

2.伪双口RAM:伪双口RAM的读写地址是独立的,可以随机选择写或读地址,同时进行读写操作。代码如下

module top
(
    input[7:0] data,
    input[5:0] write_addr,
    input[5:0] read_addr,
    input wr,
    input rd,
    input clk,
    output reg[7:0] q
);
reg [7:0] ram[63:0]; //declare ram
reg [5:0] addr_reg;  //addr register

always @(posedge clk)
begin
    if(wr)
        ram[write_addr]<=data;
    if(rd)
        q<=ram[read_addr];
end
endmodule

3.真双口RAM:真双口RAM有两套控制线,数据线,允许两个系统对其进行读写操作,代码入下:

module top
(
    input [7:0] data_a,data_b,
    input [5:0] addr_a,addr_b,
    input wr_a,wr_b,
    input rd_a,rd_b,
    input clk,
    output reg[7:0] q_a,q_b
);

reg[7:0] ram[63:0];  //declare ram
//port A
always @ (posedge clk)
begin
    if(wr_a)
        begin
        ram[addr_a]<=data_a;
        q_a<=data_a;
        end
    if(rd_a)
    q_a<=ram[addr_a];
end
//port B
always @ (posedge clk)
begin
    if(wr_b)
        begin
        ram[addr_b]<=data_b;
        q_b<=data_b;
        end
    if(rd_b)
    q_b<=ram[addr_b];
end
endmodule

4.单口ROM:ROM是用来存储数据的,可以按照下列代码形式初始化ROM,但这种方法处理大容量的ROM就比较麻烦,建议用FPGA自带的ROM IP核实现,并添加初始化文件。代码实现:

module top
(
    input [3:0] addr,
    input clk,
    output reg [7:0] q
);
reg [7:0] rom [15:0]; //declare rom
always @(addr)
begin
    case(addr)
       4’d0:rom[addr]=8'd15;
       4'd1:rom[addr]=8'd24;
       4'd2:rom[addr]=8'd100;
       4'd3:rom[addr]=8'd78;
       4'd4:rom[addr]=8'd98;
       4'd5:rom[addr]=8'd105;
       4'd6:rom[addr]=8'd86;
       4'd7:rom[addr]=8'd254;
       4'd8:rom[addr]=8'd76;
       4'd9:rom[addr]=8'd35;
       4'd10:rom[addr]=8'd120;
       4'd11:rom[addr]=8'd85;
       4'd12:rom[addr]=8'd37;
       4'd13:rom[addr]=8'd19;
       4'd14:rom[addr]=8'd22;
       4'd15:rom[addr]=8'd67;
    endcase
end
always @(posedge clk)
begin
    q<=rom[addr];
end
endmodule

5.有限状态机:在verilog里经常会用到有限状态机,处理相对复杂的逻辑,设定好不同的状态,根据触发条件跳转到对应的状态,在不同的状态下做相应的处理。有限状态机主要用到always及case语句。状态机有两种写法:一段式和三段式。一段式只用一个always语句,所有的状态转移,判断状态转移条件,数据输出都在一个always语句里,缺点是如果状态会多,会使整段程序显得冗长。三段式写法,状态转移用一个always语句,判断状态转移条件式组合逻辑,采用了一个always语句,数据输出也是单独得always语句,这样写起来比较直观清晰,状态很多时也不会显得繁琐。

Mealy有限状态机,输出不仅与当前状态有关,也与输入信号有关,一段式写法如下:

module top
(
    input shift_start,
    input shift_stop,
    input rst,
    input clk,
    input d,
    output reg [7:0] q
);

parameter Idle =2'd0; //Idle state
parameter Start=2'd1; //Start state
parameter Run  =2'd2; //Run state
parameter Stop =2'd3; //Stop state

reg[1:0] state;  //statement
reg[4:0] delay_cnt; //delay counter

always @(posedge clk or negedge rst)
begin
    if(!rst)
    begin
    state<=Idle;
    delay_cnt<=0;
    q<=0;
    end
    else
       case(state)
        Idle: begin
            if(shift_start)
                state<=Start;
        end
        Start:begin
            if(delay_cnt==5'd99)
            begin
                delay_cnt<=0;
                state<=Run;
            end
            else
                delay_cnt<=delay_cnt+1'b1;
            end
        Run :begin
            if(shift_stop)
                state<=Stop;
            else
                q<={q[6:0],d};
            end
        Stop:begin
            q<=0;
            state<=Idle;
            end
        default :state<=Idle;
        endcase
end
endmodule

Moore有限状态机,输出只与当前状态有关,与输入信号无关,输入信号只影响状态得改变,不影响输出,比如对delay_cnt和q得处理,只与state状态有关。三段式写法代码如下

module top
(
    input shift_start,
    input shift_stop,
    input rst,
    input clk,
    input d,
    output reg[7:0] q
);

parameter Idle=2'd0;  //Idle state
parameter State=2'd1; //Start state
parameter Run=2'd2;   //Run state
parameter Stop=2'd3;  //Stop state

reg[1:0] currernt_state;    //statement
reg[1:0] next_state;
reg[4:0] delay_cnt;         //delay counter

//First part:statement transition
always @(posedge clk or negedge rst)
begin
    if(!rst)
    current_state<=Idle;
    else
    current_state<=next_state;
end
//Second part:combination logic,judge statement transition condition
always @(*)
begin
    case(current_state)
    Idle :begin
        if(shift_start)
            next_state<=Start;
        else
            next_state<=Idle;
    end
    Start :begin
        if(delay_cnt==5'd99)
            next_state<=Run;
        else
            next_state<=Start;
        end
    Run: begin
        if(shift_stop)
            next_state<=Stop;
        else
            next_state<=Run;
        end
    Stop: next_state<=Idle;
    default:next_state<=Idle;
    endcase
end
//Last part:output data
always @(posedge clk or negedge rst)
begin
    if(!rst)
    delay_cnt<=0;
    else if(current_state==Start)
    delay_cnt<=delay_cnt+1'b1;
    else
    delay_cnt<=0;
end

always @(posedge clk or negedge rst)
begin
    if(!rst)
    q<=0;
    else if(current_state==Run)
    q<={q[6:0],d};
    else
    q<=0;
end
endmodule

基础模块finish!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值