Verilog-硬件描述语言使用
语言简介
Verilog在线学习HDLBits主页
基本语句
常量
1 | 3'b101 // binary 101 |
线wire
物理线路没有方向,但建模上需要指定方向。
创造一个模型,有三个输入和四个输出1
2
3
4a -> w
b -> x
b -> y
c -> z

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15module top_module(
input a,b,c,
output w,x,y,z );
assign w=a;
assign x=b;
assign y=b;
assign z=c;
// If we're certain about the width of each signal, using
// the concatenation operator is equivalent and shorter:
// assign {w,x,y,z} = {a,b,b,c};
endmodule
非门Inverter
电路中产生一个非门

1
2
3
4
5module top_module( input in, output out );
assign out = !in;
// similar
// assign out = ~in;
endmodule
与门AND gate
电路中产生一个与门

1
2
3
4
5
6
7
8module top_module(
input a,
input b,
output out );
assign out = a & b;
endmodule
或非门Nor gate
电路中产生一个或非门

1
2
3
4
5
6
7
8module top_module(
input a,
input b,
output out );
assign out = ~(a | b);
endmodule
异或非门XNOR gate
电路中产生一个异或非门

1
2
3
4
5
6
7
8module top_module(
input a,
input b,
output out );
assign out = ~(a ^ b);
endmodule
声明线Declaring wires

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18`default_nettype none
module top_module(
input a,
input b,
input c,
input d,
output out,
output out_n );
wire w1, w2, w3;
assign out = w3;
assign out_n = ~w3;
assign w3 = w1 | w2;
assign w1 = a & b;
assign w2 = c & d;
endmodule
7458芯片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17module top_module (
input p1a, p1b, p1c, p1d, p1e, p1f,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
wire wp11,wp12;
assign p1y = wp11 | wp12;
assign wp11 = p1a & p1b & p1c;
assign wp12 = p1f & p1e & p1d;
wire wp21,wp22;
assign p2y = wp21 | wp22;
assign wp21 = p2a & p2b;
assign wp22 = p2c & p2d;
endmodule
向量Vector
基本使用
声明语法:1
type [upper:lower] vector_name;
类型可以是wire, reg等等1
2
3
4
5
6wire [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.
隐含声明可能导致意想不到的结果1
2
3
4
5
6wire [2:0] a, c; // Two vectors
assign a = 3'b101; // a = 101
assign b = a; // b = 1 implicitly-created wire
assign c = b; // c = 001 <-- bug
my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared.
// This could be a bug if the port was intended to be a
unpacked数组维度跟在名称后,packed数组维度放在名称前1
2reg [7:0] mem [255:0]; // 256 unpacked elements, each of which is a 8-bit packed vector of reg.
reg mem2 [28:0]; // 29 unpacked elements, each of which is a 1-bit reg.
描绘如下电路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16module top_module (
input wire [2:0] vec,
output wire [2:0] outv,
output wire o2,
output wire o1,
output wire o0 ); // Module body starts after module declaration
assign {o2,o1,o0} = vec[2:0];
assign outv = vec;
// This is ok too
//assign o0 = vec[0];
//assign o1 = vec[1];
//assign o2 = vec[2];
endmodule
将字数据(2字节)按高低位分离1
2
3
4
5
6
7
8
9
10
11
12`default_nettype none // Disable implicit nets. Reduces some types of bugs.
module top_module(
input wire [15:0] in,
output wire [7:0] out_hi,
output wire [7:0] out_lo );
assign out_hi = in[15:8];
assign out_lo = in[7:0];
// Concatenation operator also works: assign {out_hi, out_lo} = in;
endmodule
向量门
按位与&和逻辑与&对于N-bit位输入来说,会产生不同结果。前者会产生N-bit位输出,而后者只产生1-bit位输出(作为布尔值,非0值为true,0值为false)。
描绘如下电路,out_not高5-3位为b的非,其余为a的非。

1
2
3
4
5
6
7
8
9
10
11
12
13
14module top_module(
input [2:0] a,
input [2:0] b,
output [2:0] out_or_bitwise,
output out_or_logical,
output [5:0] out_not
);
assign out_or_bitwise = a | b;
assign out_or_logical = a || b;
// ! is logical inverse
assign out_not = {~b, ~a};
endmodule
颠倒8位
1 | module top_module ( |
重复操作
声明语法:1
{num{vector}}
示例1
2
3
4{5{1'b1}} // 5'b11111 (or 5'd31 or 5'h1f)
{2{a,b,c}} // The same as {a,b,c,a,b,c}
{3'd5, {2{3'd6}}} // 9'b101_110_110. It's a concatenation of 101 with
// the second vector, which is two copies of 3'b110.
将8位数据扩展为32位,高24位为符号位(bit[7]),余下8位为输入1
2
3
4
5
6
7
8
9
10
11module top_module (
input [7:0] in,
output [31:0] out
);
// Concatenate two things together:
// 1: {in[7]} repeated 24 times (24 bits)
// 2: in[7:0] (8 bits)
assign out = { {24{in[7]}}, in };
endmodule
描述电路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15module top_module (
input a, b, c, d, e,
output [24:0] out
);
wire [24:0] top, bottom;
assign top = { {5{a}}, {5{b}}, {5{c}}, {5{d}}, {5{e}} };
assign bottom = {5{a,b,c,d,e}};
assign out = ~top ^ bottom; // Bitwise XNOR
// This could be done on one line:
// assign out = ~{ {5{a}}, {5{b}}, {5{c}}, {5{d}}, {5{e}} } ^ {5{a,b,c,d,e}};
endmodule
模块Module
基本使用
基本语法1
2
3module mod_a ( input in1, input in2, output out );
// Module body
endmodule
线与模块端口连接有两种方式:通过位置和通过名称
通过位置连接,模块实例instance1三个端口分别连接线wa, wb, wc:1
mod_a instance1 ( wa, wb, wc );
通过名称连接,模块实例instance2三个端口名称与对应线连接:1
mod_a instance2 ( .out(wc), .in1(wa), .in2(wb) );
描述电路,其中模块mod_a在其他地方描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21module top_module (
input a,
input b,
output out
);
// Create an instance of "mod_a" named "inst1", and connect ports by name:
mod_a inst1 (
.in1(a), // Port"in1"connects to wire "a"
.in2(b), // Port "in2" connects to wire "b"
.out(out) // Port "out" connects to wire "out"
// (Note: mod_a's port "out" is not related to top_module's wire "out".
// It is simply coincidence that they have the same name)
);
/*
// Create an instance of "mod_a" named "inst2", and connect ports by position:
mod_a inst2 ( a, b, out ); // The three wires are connected to ports in1, in2, and out, respectively.
*/
endmodule
移位模块
给已经实现的D触发器(D flip-flop)模块module my_dff ( input clk, input d, output q );,按下图描述电路

1
2
3
4
5
6
7
8module top_module ( input clk, input d, output q );
wire q1,q2;
my_dff d1 (clk, d, q1);
my_dff d2 (clk, q1, q2);
my_dff d3 (clk, q2, q);
endmodule
8位移位器
描绘电路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24module top_module (
input clk,
input [7:0] d,
input [1:0] sel,
output reg [7:0] q
);
wire [7:0] o1, o2, o3; // output of each my_dff8
// Instantiate three my_dff8s
my_dff8 d1 ( clk, d, o1 );
my_dff8 d2 ( clk, o1, o2 );
my_dff8 d3 ( clk, o2, o3 );
// This is one way to make a 4-to-1 multiplexer
always @(*) begin // Combinational always block
case(sel)
2'h0: q = d;
2'h1: q = o1;
2'h2: q = o2;
2'h3: q = o3;
endcase
end
endmodule
全加器
add16全加器已经定义module add16 ( input[15:0] **a**, input[15:0] **b**, input **cin**, output[15:0] **sum**, output **cout** );,它包含16个add1全加器,描述电路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17module top_module (
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire o1,o2;
add16 inst1(a[15:0],b[15:0],0,sum[15:0],o1);
add16 inst2(a[31:16],b[31:16],o1,sum[31:16],o2);
endmodule
module add1 ( input a, input b, input cin, output sum, output cout );
// Full adder module here
assign sum = cin ? ~(a^b) : a^b;
assign cout = cin ? a|b : a&b;
endmodule
加减器
减法器由全加器变化而来,只需将对第二个操作数取补码即可(反码加一)。等效的电路可以有两种效果:a+b+0和a+~b+1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14module top_module(
input [31:0] a,
input [31:0] b,
input sub,
output [31:0] sum
);
wire o1,o2;
wire [31:0] b1 = b^{32{sub}};
add16 inst1(a[15:0],b1[15:0],sub,sum[15:0],o1);
add16 inst2(a[31:16],b1[31:16],o1,sum[31:16],o2);
endmodule
过程Procedure
对于综合硬件,有两种相关always块:
- 组合型:
always @(*) - 时序型:
always @(posedge clk)
块语句需要使用begin和end标记出来,若语句只有“单句”时,块标记可以省略。
Always块
对于简单的场景,组合型always块等价于assign语句。1
2assign out1 = a & b | c ^ d;
always @(*) out2 = a & b | c ^ d;
assign语句左边符号对应的类型是net(例如:wire),而过程赋值对应的类型是variable(例如:reg)。两者的差异不会对综合的电路产生任何影响,只是一种不成文的约定。
在VHDL中有三种赋值类型:
- 持续赋值
assign x = y; - 过程阻塞赋值,在过程中使用
x = y; - 过程非阻塞赋值,在过程中使用
x <= y;
在组合alsways块中,使用阻塞赋值。在时序always块中,使用非阻塞赋值。
使用三种方式,用异或门构建电路。值得注意的是,第三种方式产生的结果会因为有触发器而产生延时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14// synthesis verilog_input_version verilog_2001
module top_module(
input clk,
input a,
input b,
output wire out_assign,
output reg out_always_comb,
output reg out_always_ff );
assign out_assign = a^b;
always @(*) out_always_comb = a^b;
always @(posedge clk) out_always_ff <= a^b;
endmodule
if语句
描绘一个2-1选择器

对应的值表
| sel_b1 | sel_b2 | out_assign out_always |
|---|---|---|
| 0 | 0 | a |
| 0 | 1 | a |
| 1 | 0 | a |
| 1 | 1 | b |
1 | // synthesis verilog_input_version verilog_2001 |
if语句锁存
修复下图错误,当cpu_overheated时,产生关闭信号(shut_off_computer设置为1)。
修改前,错误的示意图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// synthesis verilog_input_version verilog_2001
module top_module (
input cpu_overheated,
output reg shut_off_computer,
input arrived,
input gas_tank_empty,
output reg keep_driving ); //
always @(*) begin
if (cpu_overheated)
shut_off_computer = 1;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
end
endmodule
修改后1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24// synthesis verilog_input_version verilog_2001
module top_module (
input cpu_overheated,
output reg shut_off_computer,
input arrived,
input gas_tank_empty,
output reg keep_driving ); //
always @(*) begin
if (cpu_overheated)
shut_off_computer = 1;
else
shut_off_computer = 0;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
else
keep_driving = 0;
end
endmodule
case语句
基本语法1
2
3
4
5
6case (cond)
2'h0: statments0;
2'h1: statments1;
...
default: statmentsx;
endcase
建立一个6-1选择器,否则输出01
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25// synthesis verilog_input_version verilog_2001
module top_module (
input [2:0] sel,
input [3:0] data0,
input [3:0] data1,
input [3:0] data2,
input [3:0] data3,
input [3:0] data4,
input [3:0] data5,
output reg [3:0] out );//
always@(*) begin // This is a combinational circuit
case(sel)
4'h0: out = data0;
4'h1: out = data1;
4'h2: out = data2;
4'h3: out = data3;
4'h4: out = data4;
4'h5: out = data5;
default: out = 0;
endcase
end
endmodule
构建一个4bits的优先编码器,输出第一个1所在的比特位。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31module top_module (
input [3:0] in,
output reg [1:0] pos
);
always @(*) begin // Combinational always block
case (in)
4'h0: pos = 2'h0; // I like hexadecimal because it saves typing.
4'h1: pos = 2'h0;
4'h2: pos = 2'h1;
4'h3: pos = 2'h0;
4'h4: pos = 2'h2;
4'h5: pos = 2'h0;
4'h6: pos = 2'h1;
4'h7: pos = 2'h0;
4'h8: pos = 2'h3;
4'h9: pos = 2'h0;
4'ha: pos = 2'h1;
4'hb: pos = 2'h0;
4'hc: pos = 2'h2;
4'hd: pos = 2'h0;
4'he: pos = 2'h1;
4'hf: pos = 2'h0;
default: pos = 2'b0; // Default case is not strictly necessary because all 16 combinations are covered.
endcase
end
// There is an easier way to code this. See the next problem (always_casez).
endmodule
casez
当部分比特位不关心时,可以使用casez。符号z和?在一下场景都是等价的。与casez类似的是casex,后者使用符号x。1
2
3
4
5
6
7
8
9always @(*) begin
casez (in[3:0])
4'bzzz1: out = 0; // in[3:1] can be anything
4'bzz1z: out = 1;
4'b?1??: out = 2;
4'b1???: out = 3;
default: out = 0;
endcase
end
构建8bits优先编码器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// synthesis verilog_input_version verilog_2001
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*) begin
casez (in[7:0])
8'h0: pos = 0;
8'bzzzz_???1: pos = 0;
8'bzzzz_zz10: pos = 1;
8'bzzzz_z100: pos = 2;
8'bzzzz_1000: pos = 3;
8'bzzz1_0000: pos = 4;
8'bzz10_0000: pos = 5;
8'bz100_0000: pos = 6;
8'b1000_0000: pos = 7;
default: pos = 3'bzzz;
endcase
end
endmodule
键盘扫描码
处理如下四个按键
| Scancode [15:0] | Arrow key |
|---|---|
16'he06b | left arrow |
16'he072 | down arrow |
16'he074 | right arrow |
16'he075 | up arrow |
| Anything else | none |
1 | // synthesis verilog_input_version verilog_2001 |
更多特性
条件
三元条件操作符?:1
(condition ? if_true : if_false)
逻辑操作简化
若需要对向量所有位进行门操作时,正常书写比较冗余,可以对与、或和异或操作简化1
2
3& a[3:0] // AND: a[3]&a[2]&a[1]&a[0]. Equivalent to (a[3:0] == 4'hf)
| b[3:0] // OR: b[3]|b[2]|b[1]|b[0]. Equivalent to (b[3:0] != 4'h0)
^ c[2:0] // XOR: c[2]^c[1]^c[0]
实现奇偶校验位算法1
2
3
4
5
6
7module top_module (
input [7:0] in,
output parity);
assign parity = ^in[7:0];
endmodule
100bits翻转
1 | module top_module ( |
比特1计数
1 | module top_module( |
100bits全加器
1 | module top_module( |
100位BCD码全加器
已经定义1位BCD码全加器1
2
3
4
5
6module bcd_fadd (
input [3:0] a,
input [3:0] b,
input cin,
output cout,
output [3:0] sum );1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
wire [100:0] c;
assign cout = c[100];
assign c[0] = cin;
genvar i;
generate
for(i=0; i<100; ++i) begin: bcd_gen
bcd_fadd u_bcd (
.a( a[(4*i+3):(4*i)] ),
.b( b[(4*i+3):(4*i)] ),
.cin( c[i] ),
.cout( c[i+1] ),
.sum( sum[(4*i+3):(4*i)] )
);
end
endgenerate
endmodule
仿真
timescale
定义仿真中时间单位的比例。它的作用是指定时序仿真中时间单位的大小,以便仿真器可以正确地模拟设计中的时序行为。
基本语法1
`timescale unit / precision
其中,unit是时间单位,可以是1ns、1ps、1us等,表示一个时钟周期的时间长度;precision是时间精度,表示仿真的最小时间单位;
#num
等待num个时间单位后,执行后面的语句。
基本语法1
#num statement
task
Verilog语言中具有类似C语言函数的结构有task和function,他们可以增加代码可读性和重复使用性。Function用来描述组合逻辑,只能有一个返回值,function的内部不能包含时序控制。Task类似procedure,执行一段verilog代码,task中可以有任意数量的输入和输出,task也可以包含时序控制。
基本语法1
2task TASK_NAME;
endtask
PS/2键盘设备仿真
当用户按键或松开时,键盘以每帧11位的格式串行传送数据给主机,同时在PS2_CLK时钟信号上传输对应的时钟(一般为10.0–16.7kHz)。第一位是开始位(逻辑0),后面跟8位数据位(低位在前),一个奇偶校验位(奇校验)和一位停止位(逻辑1)。每位都在时钟的 下降沿 有效,下图显示了键盘传送一字节数据的时序。在下降沿有效的主要原因是下降沿正好在数据位的中间,因此可以让数据位从开始变化到接收采样时能有一段信号建立时间。
键盘通过PS2_DAT引脚发送的信息称为扫描码,每个扫描码可以由单个数据帧或连续多个数据帧构成。当按键被按下时送出的扫描码被称为 通码(Make Code) ,当按键被释放时送出的扫描码称为 断码(Break Code) 。以 W 键为例, W 键的通码是1Dh,如果 W 键被按下,则PS2_DAT引脚将输出一帧数据,其中的8位数据位为1Dh,如果 W 键一直没有释放,则不断输出扫描码1Dh 1Dh … 1Dh,直到有其他键按下或者 W 键被放开。某按键的断码是F0h加此按键的通码,如释放 W 键时输出的断码为F0h 1Dh,分两帧传输。
多个键被同时按下时,将逐个输出扫描码,如:先按左 Shift 键(扫描码为12h)、再按 W 键、放开 W 键、再放开左 Shift 键,则此过程送出的全部扫描码为:12h 1Dh F0h 1Dh F0h 12h。
键盘扫描码
每个键都有唯一的通码和断码。键盘所有键的扫描码组成的集合称为扫描码集。共有三套标准的扫描码集,所有现代的键盘默认使用第二套扫描码。下图显示了键盘各键的扫描码(以十六进制表示),如Caps键的扫描码是58h。 下图可以看出,键盘上各按键的扫描码是随机排列的,如果想迅速的将键盘扫描码转换为ASCII码,一个最简单的方法就是利用查找表 LookUp Table, LUT ,扫描码到ASCII码的转换表格请读者自己生成。

键盘控制器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55module ps2_keyboard(clk,clrn,ps2_clk,ps2_data,data,
ready,nextdata_n,overflow);
input clk,clrn,ps2_clk,ps2_data;
input nextdata_n;
output [7:0] data;
output reg ready;
output reg overflow; // fifo overflow
// internal signal, for test
reg [9:0] buffer; // ps2_data bits
reg [7:0] fifo[7:0]; // data fifo
reg [2:0] w_ptr,r_ptr; // fifo write and read pointers
reg [3:0] count; // count ps2_data bits
// detect falling edge of ps2_clk
reg [2:0] ps2_clk_sync;
always @(posedge clk) begin
ps2_clk_sync <= {ps2_clk_sync[1:0],ps2_clk};
end
wire sampling = ps2_clk_sync[2] & ~ps2_clk_sync[1];
always @(posedge clk) begin
if (clrn == 0) begin // reset
count <= 0; w_ptr <= 0; r_ptr <= 0; overflow <= 0; ready<= 0;
end
else begin
if ( ready ) begin // read to output next data
if(nextdata_n == 1'b0) //read next data
begin
r_ptr <= r_ptr + 3'b1;
if(w_ptr==(r_ptr+1'b1)) //empty
ready <= 1'b0;
end
end
if (sampling) begin
if (count == 4'd10) begin
if ((buffer[0] == 0) && // start bit
(ps2_data) && // stop bit
(^buffer[9:1])) begin // odd parity
fifo[w_ptr] <= buffer[8:1]; // kbd scan code
w_ptr <= w_ptr+3'b1;
ready <= 1'b1;
overflow <= overflow | (r_ptr == (w_ptr + 3'b1));
end
count <= 0; // for next
end else begin
buffer[count] <= ps2_data; // store ps2_data
count <= count + 3'b1;
end
end
end
end
assign data = fifo[r_ptr]; //always set output data
endmodule
键盘仿真模型1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30`timescale 1ns / 1ps
module ps2_keyboard_model(
output reg ps2_clk,
output reg ps2_data
);
parameter [31:0] kbd_clk_period = 60;
initial ps2_clk = 1'b1;
task kbd_sendcode;
input [7:0] code; // key to be sent
integer i;
reg[10:0] send_buffer;
begin
send_buffer[0] = 1'b0; // start bit
send_buffer[8:1] = code; // code
send_buffer[9] = ~(^code); // odd parity bit
send_buffer[10] = 1'b1; // stop bit
i = 0;
while( i < 11) begin
// set kbd_data
ps2_data = send_buffer[i];
#(kbd_clk_period/2) ps2_clk = 1'b0;
#(kbd_clk_period/2) ps2_clk = 1'b1;
i = i + 1;
end
end
endtask
endmodule
键盘测试代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54`timescale 1ns / 1ps
module keyboard_sim;
/* parameter */
parameter [31:0] clock_period = 10;
/* ps2_keyboard interface signals */
reg clk,clrn;
wire [7:0] data;
wire ready,overflow;
wire kbd_clk, kbd_data;
reg nextdata_n;
ps2_keyboard_model model(
.ps2_clk(kbd_clk),
.ps2_data(kbd_data)
);
ps2_keyboard inst(
.clk(clk),
.clrn(clrn),
.ps2_clk(kbd_clk),
.ps2_data(kbd_data),
.data(data),
.ready(ready),
.nextdata_n(nextdata_n),
.overflow(overflow)
);
initial begin /* clock driver */
clk = 0;
forever
#(clock_period/2) clk = ~clk;
end
initial begin
clrn = 1'b0; #20;
clrn = 1'b1; #20;
model.kbd_sendcode(8'h1C); // press 'A'
#20 nextdata_n =1'b0; #20 nextdata_n =1'b1;//read data
model.kbd_sendcode(8'hF0); // break code
#20 nextdata_n =1'b0; #20 nextdata_n =1'b1; //read data
model.kbd_sendcode(8'h1C); // release 'A'
#20 nextdata_n =1'b0; #20 nextdata_n =1'b1; //read data
model.kbd_sendcode(8'h1B); // press 'S'
#20 model.kbd_sendcode(8'h1B); // keep pressing 'S'
#20 model.kbd_sendcode(8'h1B); // keep pressing 'S'
model.kbd_sendcode(8'hF0); // break code
model.kbd_sendcode(8'h1B); // release 'S'
#20;
$stop;
end
endmodule
电路
组合逻辑
基本门
GND1
assign out = 1'b0;
NOR1
assign out = ~(in1|in2);
多路选择器
9路选择器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26module top_module(
input [15:0] a, b, c, d, e, f, g, h, i,
input [3:0] sel,
output [15:0] out );
// Case statements can only be used inside procedural blocks (always block)
// This is a combinational circuit, so use a combinational always @(*) block.
always @(*) begin
out = '1; // '1 is a special literal syntax for a number with all bits set to 1.
// '0, 'x, and 'z are also valid.
// I prefer to assign a default value to 'out' instead of using a
// default case.
case (sel)
4'h0: out = a;
4'h1: out = b;
4'h2: out = c;
4'h3: out = d;
4'h4: out = e;
4'h5: out = f;
4'h6: out = g;
4'h7: out = h;
4'h8: out = i;
endcase
end
endmodule
4位256路选择器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17module top_module (
input [1023:0] in,
input [7:0] sel,
output [3:0] out
);
// We can't part-select multiple bits without an error, but we can select one bit at a time,
// four times, then concatenate them together.
assign out = {in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]};
// Alternatively, "indexed vector part select" works better, but has an unfamiliar syntax:
// assign out = in[sel*4 +: 4]; // Select starting at index "sel*4", then select a total width of 4 bits with increasing (+:) index number.
// assign out = in[sel*4+3 -: 4]; // Select starting at index "sel*4+3", then select a total width of 4 bits with decreasing (-:) index number.
// Note: The width (4 in this case) must be constant.
endmodule