赞
踩
最近学习了verilog基本语法,总结如下
Verilog HDL 有下列四种基本的值来表示硬件电路中的电平逻辑:
整数数值表示方法
十进制('d 或 'D),十六进制('h 或 'H),二进制('b 或 'B),八进制('o 或 'O)
指明位宽
4'b1011 // 4bit 数值
32'h3022_c0de // 32bit 的数值
下划线 _
是为了增强代码的可读性。
不指明位宽
counter = 'd100 ; //一般会根据编译器自动分频位宽,常见的为32bit
counter = 100 ;
counter = 32'h64 ;
wire
表示硬件单元间的物理连线
reg
存储单元,保持数据原有的值直到被改写(寄存器)
integer
整数
real
实数
time
时间
parameter
常量/参数
数组:在 Verilog 中允许声明 reg
, wire
, integer
, time
, real
及其向量类型的数组。
integer flag [7:0] ; //8个整数组成的数组
reg [3:0] counter [3:0] ; //由4个4bit计数器组成的数组
wire [7:0] addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组
reg [31:0] data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组
reg
数组构成存储器
reg membit[0:255] ; //256bit的1bit存储器
reg [7:0] mem[0:1023] ; //1Kbyte存储器,位宽8bit
mem[511] = 8'b0 ; //令第512个8bit的存储单元值为0
字符串保存在 reg 类型的变量中,每个字符占用一个字节(8bit)。因此寄存器变量的宽度应该足够大,以保证不会溢出。
reg [0: 14*8-1] str ;
initial begin
str = "run.runoob.com";
end
操作符 | 操作符号 | 优先级 |
---|---|---|
单目运算 | + - ! ~ | 最高 |
乘、除、取模 | * / % | |
加减 | + - | |
移位 | << >> | |
关系 | < <= > >= | |
等价 | == != === !=== | |
归约 | & ~& | |
^ ~^ | ||
| ~| | ||
逻辑 | && | |
|| | ||
条件 | ?: | 最低 |
乘(*)、除(/)、加(+)、减(-)、求幂(**)、取模(%)。
注意不要溢出
大于(>),小于(<),大于等于(>=),小于等于(<=)
如果操作数中有一位为 x 或 z,则关系表达式的结果为 x。
A = 4 ;
B = 3 ;
X = 3'b1xx ;
A > B //为真
A <= B //为假
A >= Z //为X,不确定
逻辑相等(==),逻辑不等(!=),全等(===),非全等(!==)
逻辑相等/不等操作符不能比较 x 或 z,当操作数包含一个 x 或 z,则结果为不确定值。
全等比较时,如果按位比较有相同的 x 或 z,返回结果也可以为 1,即全等比较可比较 x 或 z。所以,全等比较的结果一定不包含 x
A = 4 ;
B = 8'h04 ;
C = 4'bxxxx ;
D = 4'hx ;
A == B //为真
A == (B + 1) //为假
A == C //为X,不确定
A === C //为假,返回值为0
C === D //为真,返回值为1
&&(逻辑与), ||(逻辑或),!(逻辑非)。
逻辑操作符的计算结果是一个 1bit 的值,0 表示假,1 表示真,x 表示不确定。
如果一个操作数不为 0,它等价于逻辑 1;如果一个操作数等于 0,它等价于逻辑 0。如果它任意一位为 x 或 z,它等价于 x。
A = 3;
B = 0;
C = 2'b1x ;
A && B // 为假
A || B // 为真
! A // 为假
! B // 为真
A && C // 为X,不确定
A || C // 为真,因为A为真
(A==2) && (! B) //为真,此时第一个操作数为表达式
取反(),与(&),或(|),异或(^),同或(^)
按位操作符对 2 个操作数的每 1bit 数据进行按位操作。
如果 2 个操作数位宽不相等,则用 0 向左扩展补充较短的操作数。
取反操作符只有一个操作数,它对操作数的每 1bit 数据进行取反操作。
A = 4'b0101 ;
B = 4'b1001 ;
C = 4'bx010 ;
~A //4'b1010
A & B //4'b0001
A | B //4'b1101
A^B //4'b1100
A ~^ B //4'b0011
B | C //4'b1011
B&C //4'bx000
归约与(&),归约与非(&),归约或(|),归约或非(|),归约异或(),归约同或(~)
归约操作符只有一个操作数,它对这个向量操作数逐位进行操作,最终产生一个 1bit 结果。
A = 4'b1010 ;
&A ; //结果为 1 & 0 & 1 & 0 = 1'b0,可用来判断变量A是否全1
~|A ; //结果为 ~(1 | 0 | 1 | 0) = 1'b0, 可用来判断变量A是否为全0
^A ; //结果为 1 ^ 0 ^ 1 ^ 0 = 1'b0
移位操作符包括左移(<<),右移(>>),算术左移(<<<),算术右移(>>>)。
移位操作符是双目操作符,两个操作数分别表示要进行移位的向量信号(操作符左侧)与移动的位数(操作符右侧)。
算术左移和逻辑左移时,右边低位会补 0。
逻辑右移时,左边高位会补 0;而算术右移时,左边高位会补充符号位(正/负),以保证数据缩小后值的正确性。
A = 4'b1100 ;
B = 4'b0010 ;
A = A >> 2 ; //结果为 4'b0011
A = A << 1; //结果为 4'b1000
A = A <<< 1 ; //结果为 4'b1000
C = B + (A>>>2); //结果为 2 + (-4/4) = 1, 4'b0001
用大括号 {,} 来表示,用于将多个操作数(向量)拼接成新的操作数(向量),信号间用逗号隔开。
A = 4'b1010 ;
B = 1'b1 ;
Y1 = {B, A[3:2], A[0], 4'h3 }; //结果为Y1='b1100_0011
Y2 = {4{B}, 3'd4}; //结果为 Y2=7'b111_1100
Y3 = {32{1'b0}}; //结果为 Y3=32h0,常用作寄存器初始化时匹配位宽的赋初值
condition_expression ? true_expression : false_expression
//define
`define
// ifdef ...
// else
// endif
`ifndef WINDOW
parameter DATA_DW = 32 ;
`else
parameter DATA_DW = 64 ;
`endif
//include
`include "../../param.v"
`include "header.v"
timescale
在 Verilog 模型中,时延有具体的单位时间表述,并用 `timescale 编译指令将时间单位与实际时间相关联。
该指令用于定义时延、仿真的单位和精度,格式为:
`timescale time_unit / time_precision
time_unit 表示时间单位,time_precision 表示时间精度,它们均是由数字以及单位 s(秒),ms(毫秒),us(微妙),ns(纳秒),ps(皮秒)和 fs(飞秒)组成。时间精度可以和时间单位一样,但是时间精度大小不能超过时间单位大小
`timescale 1ns/100ps //时间单位为1ns,精度为100ps,合法
//`timescale 100ps/1ns //不合法
module AndFunc(Z, A, B);
output Z;
input A, B ;
assign #5.207 Z = A & B
endmodule
ATTITION
在编译过程中,`timescale 指令会影响后面所有模块中的时延值,直至遇到另一个 `timescale 指令或 `resetall 指令。
由于在 Verilog 中没有默认的 `timescale,如果没有指定 `timescale,Verilog 模块就有会继承前面编译模块的 `timescale 参数。有可能导致设计出错。
过程结构语句有 2 种,initial 与 always 语句。它们是行为级建模的 2 种基本语句。
一个模块中可以包含多个 initial 和 always 语句,但 2 种语句不能嵌套使用。
这些语句在模块间并行执行,与其在模块的前后顺序没有关系。
但是 initial 语句或 always 语句内部可以理解为是顺序执行的(非阻塞赋值除外)。
每个 initial 语句或 always 语句都会产生一个独立的控制流,执行时间都是从 0 时刻开始。
initial 语句从 0 时刻开始执行,只执行一次,多个 initial 块之间是相互独立的。
如果 initial 块内包含多个语句,需要使用关键字 begin 和 end 组成一个块语句。
如果 initial 块内只要一条语句,关键字 begin 和 end 可使用也可不使用。
initial 理论上来讲是不可综合的,多用于初始化、信号检测等。
`timescale 1ns/1ns module test ; reg ai, bi ; initial begin ai = 0 ; #25 ; ai = 1 ; #35 ; ai = 0 ; //absolute 60ns #40 ; ai = 1 ; //absolute 100ns #10 ; ai = 0 ; //absolute 110ns end initial begin bi = 1 ; #70 ; bi = 0 ; //absolute 70ns #20 ; bi = 1 ; //absolute 90ns end //at proper time stop the simulation initial begin forever begin #100; //$display("---gyc---%d", $time); if ($time >= 1000) begin $finish ; end end end endmodule
always 语句是重复执行的。always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。
不同always块同步执行
由于循环执行的特点,always 语句多用于仿真时钟的产生,信号行为的检测等。
下面用 always 产生一个 100MHz 时钟源,并在 1010ns 时停止仿真代码如下。
`timescale 1ns/1ns module test ; parameter CLK_FREQ = 100 ; //100MHz parameter CLK_CYCLE = 1e9 / (CLK_FREQ * 1e6) ; //switch to ns reg clk ; initial clk = 1'b0 ; //clk is initialized to "0" always # (CLK_CYCLE/2) clk = ~clk ; //generating a real clock by reversing always begin #10; if ($time >= 1000) begin $finish ; end end endmodule
#组合逻辑 #阻塞赋值
连续赋值语句是 Verilog 数据流建模的基本语句,用于对 wire
型变量进行赋值。:
assign LHS_target = RHS_expression;
特点
wire Cout, A, B ;
assign Cout = A & B ; //实现计算A与B的功能
设计全加器
module full_adder1(
input Ai, Bi, Ci
output So, Co);
assign {Co, So} = Ai + Bi + Ci ;
endmodule
#并行 #非阻塞赋值
非阻塞赋值属于并行执行语句,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。
非阻塞赋值语句使用小于等于号 <=
作为赋值符。
对比
`timescale 1ns/1ns module test ; reg [3:0] ai, bi ; reg [3:0] ai2, bi2 ; reg [3:0] value_blk ; reg [3:0] value_non ; reg [3:0] value_non2 ; initial begin ai = 4'd1 ; //(1) bi = 4'd2 ; //(2) ai2 = 4'd7 ; //(3) bi2 = 4'd8 ; //(4) #20 ; //(5) //non-block-assigment with block-assignment ai = 4'd3 ; //(6) bi = 4'd4 ; //(7) value_blk = ai + bi ; //(8) value_non <= ai + bi ; //(9) //non-block-assigment itself ai2 <= 4'd5 ; //(10) bi2 <= 4'd6 ; //(11) value_non2 <= ai2 + bi2 ; //(12) end //stop the simulation always begin #10 ; if ($time >= 1000) $finish ; end endmodule
对比
always @(posedge clk) begin
a = b ;
end
always @(posedge clk) begin
b = a;
end
always @(posedge clk) begin
a <= b ;
end
always @(posedge clk) begin
b <= a;
end
常规时延
语句需要等待一定时间,然后将计算结果赋值给目标信号。
格式为:#delay procedural_statement;
reg value_test ;
reg value_general ;
#10 value_general = value_test ;
内嵌时延
遇到内嵌延时时,该语句先将计算结果保存,然后等待一定的时间后赋值给目标信号。
reg value_test ;
reg value_embed ;
value_embed = #10 value_test ;
在 Verilog 中,事件是指某一个 reg 或 wire 型变量发生了值的变化。
基于事件触发的时序控制又主要分为以下几种。
一般事件控制
事件控制用符号 `@ 表示。
语句执行的条件是信号的值发生特定的变化。
关键字 posedge
指信号发生边沿正向跳变,negedge
指信号发生负向边沿跳变,未指明跳变方向时,则 2 种情况的边沿变化都会触发相关事件。
//信号clk只要发生变化,就执行q<=d,双边沿D触发器模型
always @(clk) q <= d ;
//在信号clk上升沿时刻,执行q<=d,正边沿D触发器模型
always @(posedge clk) q <= d ;
//在信号clk下降沿时刻,执行q<=d,负边沿D触发器模型
always @(negedge clk) q <= d ;
//立刻计算d的值,并在clk上升沿时刻赋值给q,不推荐这种写法
q = @(posedge clk) d ;
命名事件控制
用户可以声明 event(事件)类型的变量,并触发该变量来识别该事件是否发生。命名事件用关键字 event 来声明,触发信号用 ->
表示。
event start_receiving ;
always @( posedge clk_samp) begin
-> start_receiving ; //采样时钟上升沿作为时间触发时刻
end
always @(start_receiving) begin
data_buf = {data_if[0], data_if[1]} ; //触发时刻,对多维数据整合
end
敏感列表
当多个信号或事件中任意一个发生变化都能够触发语句的执行时,Verilog 中使用"或"表达式来描述这种情况,用关键字 or
连接多个事件或信号。这些事件或信号组成的列表称为"敏感列表"。当然,or
也可以用逗号,
来代替。例如:
//带有低有效复位端的D触发器模型
always @(posedge clk or negedge rstn) begin
//always @(posedge clk , negedge rstn) begin
//也可以使用逗号陈列多个事件触发
if(! rstn)begin
q <= 1'b ;
end
else begin
q <= d ;
end
end
电平作为敏感信号来控制时序,即后面语句的执行需要等待某个条件为真。Verilog 中使用关键字 wait
来表示这种电平敏感情况。例如:
initial begin
wait (start_enable) ; //等待 start 信号
forever begin
//start信号使能后,在clk_samp上升沿,对数据进行整合
@(posedge clk_samp) ;
data_buf = {data_if[0], data_if[1]} ;
end
end
begin end
顺序块用关键字 begin
和 end
来表示。
顺序块中的语句是一条条执行的。当然,非阻塞赋值除外。
顺序块中每条语句的时延总是与其前面语句执行的时间相关。
fork join
并行块有关键字 fork
和 join
来表示
并行块中的语句是并行执行的,即便是阻塞形式的赋值。
并行块中每条语句的时延都是与块语句开始执行的时间相关。
我们可以给块语句结构命名。
命名的块中可以声明局部变量,通过层次名引用的方法对变量进行访问。
`timescale 1ns/1ns module test; initial begin: runoob //命名模块名字为runoob,分号不能少 integer i ; //此变量可以通过test.runoob.i 被其他模块使用 i = 0 ; forever begin #10 i = i + 10 ; end end reg stop_flag ; initial stop_flag = 1'b0 ; always begin : detect_stop if ( test.runoob.i == 100) begin //i累加10次,即100ns时停止仿真 $display("Now you can stop the simulation!!!"); stop_flag = 1'b1 ; end #10 ; end endmodule
命名的块也可以被禁用,用关键字 disable 来表示。
disable 可以终止命名块的执行,可以用来从循环中退出、处理错误等。
与 C 语言中 break 类似,但是 break 只能退出当前所在循环,而 disable 可以禁用设计中任何一个命名的块。
`timescale 1ns/1ns module test; initial begin: runoob_d //命名模块名字为runoob_d integer i_d ; i_d = 0 ; while(i_d<=100) begin: runoob_d2 # 10 ; if (i_d >= 50) begin //累加5次停止累加 disable runoob_d3.clk_gen ;//stop 外部block: clk_gen disable runoob_d2 ; //stop 当前block: runoob_d2 end i_d = i_d + 10 ; end end reg clk ; initial begin: runoob_d3 while (1) begin: clk_gen //时钟产生模块 clk=1 ; #10 ; clk=0 ; #10 ; end end endmodule
if...else
结合 begin...end
if(en) begin
if(sel == 2'b1) begin
sout = p1s ;
end
else begin
sout = p0 ;
end
end
case
case(case_expr)
condition1 : true_statement1 ;
condition2 : true_statement2 ;
……
default : default_statement ;
endcase
四路选择器
module mux4to1( input [1:0] sel , input [1:0] p0 , input [1:0] p1 , input [1:0] p2 , input [1:0] p3 , output [1:0] sout); reg [1:0] sout_t ; always @(*) case(sel) 2'b00: begin sout_t = p0 ; end 2'b01: sout_t = p1 ; 2'b10: sout_t = p2 ; default: sout_t = p3 ; endcase assign sout = sout_t ; endmodule
包含 while,for,repeat,和 forever 循环。循环语句只能在 always 或 initial 块中使用,但可以包含延迟表达式。
//while 循环中止条件为 condition 为假。 //如果开始执行到 while 循环时 condition 已经为假,那么循环语句一次也不会执行。 while (condition) begin … end //initial_assignment 为初始条件。 //condition 为终止条件,condition 为假时,立即跳出循环。 //step_assignment 为改变控制变量的过程赋值语句,通常为增加或减少循环变量计数。 for(initial_assignment; condition ; step_assignment) begin … end //repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。 repeat (loop_times) begin … end //forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever。 //forever 相当于 while(1) 。 forever begin … end
module module_name
#(parameter_list)
(port_list) ;
Declarations_and_Statements ;
endmodule
![[Pasted image 20230510212756.png]]
端口是模块与外界交互的接口。对于外部环境来说,模块内部是不可见的,对模块的调用只能通过端口连接进行。
端口列表
模块的定义中包含一个可选的端口列表,一般将不带类型、不带位宽的信号变量罗列在模块声明里。
module pad(
DIN, OEN, PULL,
DOUT, PAD);
端口声明
端口信号在端口列表中罗列出来以后,就可以在模块实体中进行声明了。
端口类型有 3 种: 输入(input),输出(output)和双向端口(inout)。
input、inout 类型不能声明为 reg 数据类型,因为 reg 类型是用于保存数值的,而输入端口只能反映与其相连的外部信号的变化,不能保存这些信号的值。
output 可以声明为 wire 或 reg 数据类型。
eg:
module pad(
input DIN, OEN ,
input [1:0] PULL ,
inout PAD ,
output reg DOUT
);
module pad(
input DIN, OEN ,
input [1:0] PULL ,
inout PAD ,
output DOUT
);
reg DOUT ;
eg
module full_adder1( input Ai, Bi, Ci output So, Co); assign {Co, So} = Ai + Bi + Ci ; endmodule //例化 full_adder1 u_adder0( .Ai (a[0]), .Bi (b[0]), .Ci (c==1'b1 ? 1'b0 : 1'b1), .So (so_bit0), .Co (co_temp[0])); //输出端口可悬空或删除 //output 端口 Co 悬空 full_adder1 u_adder0( .Ai (a[0]), .Bi (b[0]), .Ci (c==1'b1 ? 1'b0 : 1'b1), .So (so_bit0), .Co ()); //output 端口 Co 删除 full_adder1 u_adder0( .Ai (a[0]), .Bi (b[0]), .Ci (c==1'b1 ? 1'b0 : 1'b1), .So (so_bit0)); //顺序实例化,需一一对应 full_adder1 u_adder1( a[1], b[1], co_temp[0], so_bit1, co_temp[1]);
例化模块时,将新的参数值写入模块例化语句,以此来改写原有 module 的(parameter
)参数值。
eg:
module ram #( parameter AW = 2 , parameter DW = 3 ) ( input CLK , input [AW-1:0] A , input [DW-1:0] D , input EN , input WR , //1 for write and 0 for read output reg [DW-1:0] Q ); reg [DW-1:0] mem [0:(1<<AW)-1] ; always @(posedge CLK) begin if (EN && WR) begin mem[A] <= D ; end else if (EN && !WR) begin Q <= mem[A] ; end end endmodule //带参数例化,改变parameter从而改变位宽和字节 ram #(.AW(4), .DW(4)) u_ram ( .CLK (clk), .A (a[AW-1:0]), .D (d), .EN (en), .WR (wr), //1 for write and 0 for read .Q (q) );
函数只能在模块中定义,位置任意,并在模块的任何地方引用,作用范围也局限于此模块。函数主要有以下几个特点:
function [range-1:0] function_id ;
input_declaration ;
other_declaration ;
procedural_statement ;
endfunction
函数的返回值通过这个function_id
变量进行传递。(有点像matlab的函数)
eg:数据大小端转换
module endian_rvs #(parameter N = 4) ( input en, //enable control input [N-1:0] a , output [N-1:0] b ); reg [N-1:0] b_temp ; always @(*) begin if (en) begin b_temp = data_rvs(a); end else begin b_temp = 0 ; end end assign b = b_temp ; //function entity function [N-1:0] data_rvs ; input [N-1:0] data_in ; parameter MASK = 32'h3 ; integer k ; begin for(k=0; k<N; k=k+1) begin data_rvs[N-k-1] = data_in[k] ; end end endfunction endmodule
module
里的module
?
有限状态机(Finite-State Machine,FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。状态机不仅是一种电路的描述工具,而且也是一种思想方法,在电路设计的系统级和 RTL 级有着广泛的应用。
Verilog 中状态机主要用于同步时序逻辑的设计,能够在有限个状态之间按一定要求和规律切换时序电路的状态。状态的切换方向不但取决于各个输入值,还取决于当前所在状态。 状态机可分为 2 类:Moore 状态机和 Mealy 状态机。
Moore 型状态机
Moore 型状态机的输出只与当前状态有关,与当前输入无关。
输出会在一个完整的时钟周期内保持稳定,即使此时输入信号有变化,输出也不会变化。输入对输出的影响要到下一个时钟周期才能反映出来。这也是 Moore 型状态机的一个重要特点:输入与输出是隔离开来的。
Mealy 型状态机
Mealy 型状态机的输出,不仅与当前状态有关,还取决于当前的输入信号。
Mealy 型状态机的输出是在输入信号变化以后立刻发生变化,且输入变化可能出现在任何状态的时钟周期内。因此,同种逻辑下,Mealy 型状态机输出对输入的响应会比 Moore 型状态机早一个时钟周期。
状态机设计如下:
eg:
自动售卖机
自动售卖机的功能描述如下:
饮料单价 2 元,该售卖机只能接受 0.5 元、1 元的硬币。考虑找零和出货。投币和出货过程都是一次一次的进行,不会出现一次性投入多币或一次性出货多瓶饮料的现象。每一轮售卖机接受投币、出货、找零完成后,才能进入到新的自动售卖状态。
// vending-machine // 2 yuan for a bottle of drink // only 2 coins supported: 5 jiao and 1 yuan // finish the function of selling and changing module vending_machine_p3 ( input clk , input rstn , input [1:0] coin , //01 for 0.5 jiao, 10 for 1 yuan output [1:0] change , output sell //output the drink ); //machine state decode parameter IDLE = 3'd0 ; parameter GET05 = 3'd1 ; parameter GET10 = 3'd2 ; parameter GET15 = 3'd3 ; //machine variable reg [2:0] st_next ; reg [2:0] st_cur ; //(1) state transfer always @(posedge clk or negedge rstn) begin if (!rstn) begin st_cur <= 'b0 ; end else begin st_cur <= st_next ; end end //(2) state switch, using block assignment for combination-logic //all case items need to be displayed completely always @(*) begin //st_next = st_cur ;//如果条件选项考虑不全,可以赋初值消除latch case(st_cur) IDLE: case (coin) 2'b01: st_next = GET05 ; 2'b10: st_next = GET10 ; default: st_next = IDLE ; endcase GET05: case (coin) 2'b01: st_next = GET10 ; 2'b10: st_next = GET15 ; default: st_next = GET05 ; endcase GET10: case (coin) 2'b01: st_next = GET15 ; 2'b10: st_next = IDLE ; default: st_next = GET10 ; endcase GET15: case (coin) 2'b01,2'b10: st_next = IDLE ; default: st_next = GET15 ; endcase default: st_next = IDLE ; endcase end //(3) output logic, using non-block assignment reg [1:0] change_r ; reg sell_r ; always @(posedge clk or negedge rstn) begin if (!rstn) begin change_r <= 2'b0 ; sell_r <= 1'b0 ; end else if ((st_cur == GET15 && coin ==2'h1) || (st_cur == GET10 && coin ==2'd2)) begin change_r <= 2'b0 ; sell_r <= 1'b1 ; end else if (st_cur == GET15 && coin == 2'h2) begin change_r <= 2'b1 ; sell_r <= 1'b1 ; end else begin change_r <= 2'b0 ; sell_r <= 1'b0 ; end end assign sell = sell_r ; assign change = change_r ; endmodule
参考文献
https://www.runoob.com/w3cnote_genre/verilog
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。