Verilog语法基础
1 前言
1.1 不可综合
Verilog 是描述硬件电路的,其建立在硬件电路的基础之上。而有些语法结构只是以仿真测试为目的,是不能与实际硬件电路对应起来的。也就是说在使用这些语法时,将一个语言描述的程序映射成实际硬件电路中的结构是不能实现的,也称为不可综合语法。不可综合或不推荐使用的代码都在测试平台Testbench使用。(下文详细叙述测试平台使用)
1.2 可综合
RTL推荐使用的代码。综合就是把编写的 RTL 代码转换成对应的实际电路。比如编写代码 assign a=b&c;EDA 综合工具就会去元件库里调用一个二输入与门,将输入端分别接上 b 和 c,输出端接上 a。“综合”要做的事情有:编译 RTL 代码,从库里选择用到的门器件,把这些器件按照“逻辑”搭建成“门”电路。
2 I/O说明、参数定义
2.1 I/O说明
在定义端口列表的时候我们知道输入用 input,输出用 output,双向端口inout。
2.2 参数定义
参数是一种常量,通常出现在 module 内部,常被用于定义状态机的状态、数据位宽和计数器计数个数大小等,和C语言,Python等语言中全局变量功能一样,方便修改。parameter 是出现在模块内部的局部定义,只作用于声明的那个文件,可以被灵活改变,这是 parameter 的一个重要特征。例如:
parameter IDLE = 3'b001;
parameter CNT_1S_WIDTH = 4'd15
Parameter CNT_MAX = 25'd24_999_999
3 变量信号
Verilog 语言中主要的两种变量类型
- 线网型:wire表示电路间的物理连接;
- 寄存器型:reg是Verilog 中一个抽象的数据存储单元。
3.1 信号位宽
基本语法:
wire/reg [msb: lsb] wire1/reg1, wire2/reg2, . . .,wireN/regN;
1)如果没有定义范围,缺省值为 1 位;
2)msb 和 lsb 定义了范围,表示了位宽。例如[7:0]是 8 位位宽,也就是可以表示成 8’b0 至 8’b
1111_1111;
3)msb 和 lsb 必须为常数
4)没有定义信号数据类型时,缺省为 wire 类型
5)注意数组类型按照降序方式,如[7:0] ,不要写成[0:7]。
定义 | 说明 |
---|---|
wire a | 位宽为 1 的 wire 型信号 a |
wire [7:0] | 表示该 wire 型信号的位宽为8位 |
wire [3:0] Sat | Sat 为 4 位线型信号 |
wire Cnt | 1 位线型信号 |
wire [31:0] Kisp, Pisp, Lisp | Kisp, Pisp, Lisp 都是 32 位的线型信号 |
reg [3:0] Sat | Sat 为 4 位寄存器型信号 |
reg Cnt | 1 位寄存器 |
reg [31:0] Kisp, Pisp, Lisp | Kisp, Pisp, Lisp 都是 32 位的寄存器信号 |
3.2 wire/reg的区别
在设计逻辑的时候要明确定义每个信号是 wire 还是 reg 属性。
1)凡是在 always 或 initial 语句中被赋值的变量(赋值号左边的变量),不论表达的是组合逻辑还是时序逻辑,都一定是 reg 型变量;
2)凡是在 assign 语句中被赋值的变量,一定是 wire 型变量。
4 数字进制
4.1 基数表示法(数字表示方式)
[换算为二进制后位宽的总长度][’][数值进制符号][与数值进制符号对应的数值]
符号 | 进制 |
---|---|
h | 十六进制 |
o | 八进制 |
b | 二进制 |
d | 十进制 |
例子:
数字表示 | 含义 |
---|---|
8’hab | 表示 8bit 的十六进制数,换算成二进制是 1010_1011 |
8’d171 | 表示 8bit 的十进制数,换算成二进制是 1010_1011 |
8’o253 | 表示 8bit 的八进制数,换算成二进制是 1010_1011 |
8’b1010_1011 | 表示 8bit 的二进制数,二进制就是 1010_1011 |
32’h12 | 等同于 32’h00000012,即数值未写完整时,高位补 0 |
4.2 二进制是基础
1和0表示高低电平 。
例如要传输送+1,-1,0,+2,线 c 表示正负,其中 0 表示正数,1 表示负数。用线 a 和线 b 表示数值。在 FPGA 设计中,不清楚小数、有符号数的计算方法的最根本原因是不清楚这些数据所对应的二进制值,只要理解了对应的二进制值,很多问题都可以解决。~~(源码、补码、反小数表示,在数字电路和计算机组成原理书中的开篇几张都有介绍,这是计算机基础不过多介绍)
4.3 不定态(X)
前文中讲过数字电路只有高电平和低电平,分别表示 1 和 0。但代码中经常能看到 x 和 z,如 1’ bx,1’bz。那么这个 x 和 z 是什么电平呢?答案是并没有实际的电平来对应两者。x 和 z 更多地是用来表示设计者的意图或者用于仿真目的,旨在告诉仿真器和综合器如何解释这段代码。
X 态,称之为不定态,其常用于判断条件,从而告诉综合工具设计者不关心它的电平是多少,是
0 还是 1 都可以。
例如:
din==4’b10x0 等价于 din == 4’b1000||din ==4’b1010
4.4 高阻态(Z)
Z 态,一般称之为高阻态,表示设计者不驱动这个信号(既不给 0 也不给 1),通常用于三态门接口当中。
1)当 CPU 和 FPGA 都不驱动该总线时,A 点保持为高电平。
2)当 FPGA 不驱动该总线
当CPU 驱动该总线时,A 点的值就由 CPU 决定。
当 CPU 不驱动该总线,FPGA 驱动该总线时,A 点的值就由 FPGA 决定。
但 FPGA 和 CPU 不能同时驱动该总线,否则 A 的电平就不确定了,通常 FPGA 和 CPU何时驱动总线是按事先协商的协议进行工作。
三态门是一个硬件,上图是它的典型结构。三态门有四个接口,如上图所示的写使能 wr_en、写
数据 wr_data、读数据 rd_data 以及与外面器件相连的三态信号 data,需要注意的是写使能信号。
1)当该信号有效时三态门会将 wr_data 的值赋给三态线 data,此时 data 的值由 wr_data 决定;
2)当 wr_data 为 0 时 data 值为 0;
3)当 wr_data 为 1 时 data 值为 1;
4)而当写使能信号无效时,则不论 wr_data 值是多少都不会对外面的 data 值有影响,也就是不驱动;
5 条件语句
5.1 条件运算符
相当于C语言的三元运算符
1)a = 6, b = 7,条件表达式(a > b) ? a : b 的结果为 7
2)a > b ? a : c > d ? c : d 相当于 a > b ? a : (c > d ? c : d)
虽然后面要讲到的 if-else 也可以实现这种功能,但是 if-else 只能在 always 块中使用,不能在 assign 中使用,如果我们想在 assign 中使用就需要用到条件运算符。
1 module mux2_1
2 (
3 input wire in1, //输入端 1
4 input wire in2, //输入端 2
5 input wire sel, //选择端
6
7 output wire out //结果输出
8 );
9
10 //out:组合逻辑输出选择结果
11 //此处使用的是条件运算符(三元运算符),当括号里面的条件成立时
12 //执行"?”后面的结果;如果括号里面的条件不成立时,执行“:”后面的结果
13 assign out = (sel == 1'b1) ? in1 : in2;
14
15 endmodule
5.2 if-else 条件分支语句
用法和C语言类似
1 module mux2_1 //模块的开头以“module”开始,然后是模块名“mux2_1”
2 (
3 input wire in1, //输入端 1,信号名后就是端口列表“();”(端口列表里
4 //面列举了该模块对外输入、输出信号的方式、类型、
5 //位宽、名字),该写法采用了 Verilog-2001 标准,这
6 //样更直观且实例化时也更方便,之前的 Verilog-1995
7 //标准是将模块对外输入、输出信号的方式、类型、位
8 //宽都放到外面
9
10 input wire in2, //输入端 2,当数据只有一位宽时位宽表示可以省略
11 //且输入只能是 wire 型变量
12
13 input wire sel, //选择端,每行信号以“,”结束,最后一个后面不加“,”
14
15 output reg out //结果输出,输出可以是 wire 型变量也可以是 reg 型变
16 //量如果输出在 always 块中被赋值(即在“<=”的左边)
17 //就要用 reg 型变量,如果输出在 assign 语句中被赋值
18 //(即在“=”的左边)就要用 wire 型变量
19 ); //端口列表括号后有个“;”不要忘记
20
21 //out:组合逻辑输出 sel 选择的结果
22 always@(*)//“*”为通配符,表示只要 if 括号中的条件或赋值号右边的变量发生变化
23 //则立即执行下面的代码,“(*)”在此 always 中等价于“(sel, in1, in2)”写法
24
25 if(sel == 1'b1)//当“if...else...”中只有一个变量时不需要加“begin...end”
26 //也显得整个代码更加简洁
27
28 out = in1; //always 块中如果表达的是组合逻辑关系时使用“=”进行赋值
29 //每句赋值以“;”结束
30 else
31 out = in2;
32
33 //模块的结尾以“endmodule”结束
1 module mux2_1 //模块的开头以“module”开始,然后是模块名“mux2_1”
2 (
3 input wire in1, //输入端 1,信号名后就是端口列表“();”(端口列表里
4 //面列举了该模块对外输入、输出信号的方式、类型、
5 //位宽、名字),该写法采用了 Verilog-2001 标准,这
6 //样更直观且实例化时也更方便,之前的 Verilog-1995
7 //标准是将模块对外输入、输出信号的方式、类型、位
8 //宽都放到外面
9
10 input wire in2, //输入端 2,当数据只有一位宽时位宽表示可以省略
11 //且输入只能是 wire 型变量
12
13 input wire sel, //选择端,每行信号以“,”结束,最后一个后面不加“,”
14
15 output reg out //结果输出,输出可以是 wire 型变量也可以是 reg 型变
16 //量如果输出在 always 块中被赋值(即在“<=”的左边)
17 //就要用 reg 型变量,如果输出在 assign 语句中被赋值
18 //(即在“=”的左边)就要用 wire 型变量
19 ); //端口列表括号后有个“;”不要忘记
20
21 //out:组合逻辑输出 sel 选择的结果
22 always@(*)//“*”为通配符,表示只要 if 括号中的条件或赋值号右边的变量发生变化
23 //则立即执行下面的代码,“(*)”在此 always 中等价于“(sel, in1, in2)”写法
24
25 if(sel == 1'b1)//当“if...else...”中只有一个变量时不需要加“begin...end”
26 //也显得整个代码更加简洁
27
28 out = in1; //always 块中如果表达的是组合逻辑关系时使用“=”进行赋值
29 //每句赋值以“;”结束
30 else
31 out = in2;
32
33 //模块的结尾以“endmodule”结束
34 //每个模块只能有一组“module”和“endmodule”,所有的代码都要在它们中间编写
35 endmodule
5.3 case 分支控制语句
用法和C语言类似
1 module Verilog
2 (
3 input wire in1, //输入端 1
4 input wire in2, //输入端 2
5 input wire sel, //选择端
6
7 output reg out //结果输出
8 );
9
10 //out:组合逻辑输出选择结果
11 always@(*)
12 case(sel)
13 1'b1 : out = in1;
14 1'b0 : out = in2;
15 //如果 sel 不能列举出所有的情况一定要加 default
16 //此处 sel 只有两种情况,并且完全列举了,所以 default 可以省略
17 default : out = in1;
18 endcase
19
20 endmodule
6 阻塞赋值和非阻塞赋值
6.1 定义
在 always 语句块中,Verilog 语言支持两种类型的赋值:阻塞赋值和非阻塞赋值。阻塞赋值使用
“=”语句;非阻塞赋值使用“<=”语句。
1)阻塞赋值:在一个“begin…end”的多行赋值语句,先执行当前行的赋值语句,再执行下一行
的赋值语句。
2)非阻塞赋值:在一个“begin…end”的多行赋值语句,在同一时间内同时赋值。
3)begin/end 相当于c语言中的大括号。
begin
c = a;
d = c + a;
end
begin
c <= a;
d <= c + a;
end
1)阻塞赋值的执行过程和结果为:程序先执行第 2 行,此时 c 的值将更新为 1,然后再执行 3 行,
此时
2)非阻塞赋值的执行过程和结果为:程序同时执行第 7 行和 8 行。需要特别注意是,在执行第 8
行的时候,第 7 行还并未执行,这也就意味着 c 的值还没有发生变化,即此时 c 的值为 0。同时执行
的结果是,c 的值为 1,d 的值为 1
6.2不同场景应用
为什么会有下面这样的规范?硬件电路没办法实现。
在组合逻辑中
写 c<=a,d<=c+a。硬件电路很难实现。组合电路想的是用老c的值赋值给d,在组合逻辑中实现的是连线的方式。很难实现。同样的道理,在时序电路中,用阻塞赋值,硬件电路也很难实现。
根据规范要求:
1)组合逻辑中应使用阻塞赋值“=”。
2)时序逻辑中应使用非阻塞赋值“<=”。
可以将这个规则牢牢记住,按照这一规则进行设计绝对不会发生错误。制定这个规范的原因并不是考虑语法需要,而是为了正确的进行硬件描述。
7 运算符
7.1 算数运算符
7.1.1 加法运算符
7.1.2 减法运算符
7.1.3 乘法运算符
7.1.4 除法和求余运算符(一般不用)
综合器可以识别除法运算符和求余运算符,但是这两种运算符包括大量的乘法、加法和减法操作,所以在 FPGA 里除法器的电路是非常大的,综合器可能无法直接转成上图所示的电路。
此处可能存在疑虑:为什么除法和求余会占用大量的资源呢?可以来分析一下十进制除法和求余的过程,以 122 除以 11 为例。
7.2 逻辑运算符
7.2.1 逻辑与
“&&”是双目运算符,其要求有两个操作数,如 a && b
- 1 位逻辑与
- 多位逻辑与
7.2.2 逻辑或
- 一位逻辑或
- 多位逻辑或
7.2.3 逻辑非
7.2.4 如何运算
如果操作数是多位的,则可以将操作数看做整体,若操作数中每一位都是 0 值则为逻辑 0 值。若操作数中有 1 则为逻辑 1。
7.3 按位逻辑运算符
真值表
7.3.1 按位与
1.单目按位与
2.双目按位与
7.3.2 按位或
1.单目按位或
2.双目按位或
7.3.3 按位非
- 单目按位非
7.3.4 双目按位异或
7.3.5 与逻辑运算符对比
7.4 关系运算符
注意!!!
在verilog通常这样写 1 <=b < 10 小于那通常没有等于
7.5 移位运算符
7.5.1 左移运算符(通过左移乘法运算)
左移操作是不消耗逻辑资源的,甚至连与门、非门都不需要,它只是线的连接。非常的节省资源。
1.不够补0(低位补0)
2. 左移操作后,要看用多少位来存储结果
执行第二段代码 01001
左移一个 10010 不够位最后补0
3. 通过左移乘法运算
注意位宽
7.5.2 右移运算符(通过右移除法运算)
1.不够补0(即在高位补 0)
a = 000001
2. 除法操作
除的必须是2的N次方
7.6 拼接运算符
拼接符是不消耗任何硬件资源的,其只是把线换一种组合方式。
8 功能描述
8.1 组合逻辑
8.1.1 assign 语句
8.1.2 always 语句
做的是连线的操作,与时钟完全不相干。
8.2 时序逻辑
8.2.1 always 语句
8.2.2 同步/异步
1)同步:同一个时钟触发信号,同一个时钟信号的触发敏感边沿。
2)异步:2个以上时钟,或者上升沿控制一个触发下降沿控制一个触发。
9 Testbench中的系统函数
9.1 timescale
`timescale 1ns/1ns //时间尺度预编译指令 时间单位/时间精度
时间单位和时间精度由值 1、10、和 100 以及单位 s、ms、us、ns、ps 和 fs 组成。
时间单位:定义仿真过程所有与时间相关量的单位。
timescale 1ns/10ps 精度 0.01
注意:时间单位不能比时间精度小
9.2 ‘#+数字’
仿真中使用“#数字”表示延时相应时间单位的时间,例: #10 表示延时 10 个单位的时间,即 10ns。
#10.11 表示延时 10.11ns 10110ps。
9.3 initial
1)initial 语句是不可以被综合的,一般只在 testbench 中表达而不在 RTL 代码中表达。
2) initial 块中的语句上电后只执行一次,主要用于初始化仿真中要输入的信号。
9.4 $display 用于输出、打印信息
1 //a,b,c 输出列表,需要输出信息的变量
2 //每次打印信息后自动换行
3 `timescale 1ns/1ns
4
5 module tb_test();
6
7 reg [3:0] a;
8 reg [3:0] b;
9 reg [3:0] c;
10
11 initial begin
12 $display("Hello");
13 $display("EmbedFire");
14 a = 4'd5;
15 b = 4'd6;
16 c = a + b;
17 #100;
18 $display("%b+%b=%d", a, b, c);
19 end
20
21 endmodule
9.5 $write 用于输出、打印信息
1 //a,b,c 为输出列表,需要输出信息的变量
2 `timescale 1ns/1ns
3 module tb_test();
4
5 reg [3:0] a;
6 reg [3:0] b;
7 reg [3:0] c;
8
9 initial begin
10 $write("hello ");
11 $write("EmbedFire\n");
12 a = 4'd5;
13 b = 4'd6;
14 c = a + b;
15 #100;
16 $write("%b+%b=%d\n",a, b, c);
17 end
18
19 endmodule
%s 输出字符 见 9.10
9.6 $strobe 用于输出、打印信息
1 //a,b,c 输出列表,需要输出信息的变量
2 //打印信息后自动换行,触发操作完成后执行
3 `timescale 1ns/1ns
4 module tb_test ();
5
6 reg [3:0] a;
7 reg [3:0] b;
8 reg [3:0] c;
9
10 initial begin
11 $strobe("strobe:%b+%b=%d", a, b, c);
12 a = 4'd5;
13 $display("display:%b+%b=%d", a, b, c);
14 b = 4'd6;
15 c = a + b;
16 end
17
18 endmodule
9.7 $monitor 用于持续监测变量
1 //a,b,c 输出列表,需要输出信息的变量
2 //被测变量变化触发打印操作,自动换行
3 `timescale 1ns/1ns
4 module tb_test ();
5
6 reg [3:0] a;
7 reg [3:0] b;
8 reg [3:0] c;
9
10 initial begin
11 a = 4'd5;
12 #100;
13 b = 4'd6;
14 #100;
15 c = a + b;
16 end
17
18 initial $monitor("%b+%b=%d", a, b, c);
19
20 endmodule
9.8 $ stop 用于暂停仿真,$ finish 用于结束仿真
1 `timescale 1ns/1ns
2
3 module tb_test();
4
5 initial begin
6 $display("Hello");
7 $display("EmbedFire");
8 #100;
9 $display("Stop Simulation");
10 $stop; //暂停仿真
11 $display("Continue Simulation");
12 #100;
13 $display("Finish Simulation");
14 $finish; //结束仿真
15 end
16
17 endmodule
9.9 $ time、$random
$ time 为时间函数,返回 64 位当前仿真时间;$random 用于产生随机函数,返回随机数。
1 `timescale 1ns/1ns
2 module tb_test ();
3
4 reg [3:0] a;
5
6 always #10 a = $random;
7
8 initial $monitor("a=%d @time %d",a,$time);
9
10 endmodule
9.10 $ readmemb、$readmemh
9.11 Testbench中与 RTL 区别
9.11.1 wire和reg 区别
1)要在 initial 块和 always 块中被赋值的变量一定要是 reg 型;
2)在 testbench 中待测试 RTL 模块的输入永远是 reg 型变量;
3)输出信号,我们直接观察,也不用在任何地方进行赋值,所以是 wire 型变量(在 testbench 中待测试 RTL 模块的输出永远是 wire 型变量);
9.11.2 begin区别
1)在仿真中 begin…end 块中的内容都是顺序执行的,在没有延时的情况下几乎没有差别,看上去是同时执行的,在没有延时的情况下几乎没有差别,看上去是同时执行的;
2)而在 rtl 代码中 begin…end 相当于括号的作用,在同一个 always 块中给多个变量赋值的时候要加上;
9.11.3 always语句直接赋值
in2:产生输入随机数,模拟输入端 2 的输入情况
1)在仿真Testbench中always #10 in2 <= {$random} % 2;
2)always 直接赋值而不是使用assign,在RTL中使用assign;
9.11.4 时钟信号采用阻塞赋值
9.11.4 区别的代码实例
还有赋值符号<=的区别,全部使用<=进行赋值
10 写一段完整 RTL和 Testbench(全加器)
10.1 RTL,全加器是在半加器的基础上写的
半加器
1 module half_adder
2 (
3 input wire in1 , //加数 1
4 input wire in2 , //加数 2
5
6 output wire sum , //两个数的加和
7 output wire cout //两个数加和后的进位
8 );
9
10 //sum:两个数加和的输出
11 //cout:两个数进位的输出
12 assign {cout, sum} = in1 + in2;
13
14 endmodule
全加器
module full_adder
2 (
3 input wire in1 , //加数 1
4 input wire in2 , //加数 2
5 input wire cin , //上一级的进位
6
7 output wire sum , //两个数的加和
8 output wire cout //加和后的进位
9 );
10
11 //wire define
12 //在顶层中作为 half_adder_inst0 的 sum 信号和 half_adder_inst1 的 in1 信号的中间连线
13 wire h0_sum ;
14
15 //在顶层中作为 half_adder_inst0 的 cout 信号和或门的中间连线
16 wire h0_cout;
17
18 //在顶层中作为 half_adder_inst1 的 cout 信号和或门的中间连线
19 wire h1_cout;
20
21 //------------------------half_adder_inst0------------------------
22 half_adder half_adder_inst0//前面是实例化(调用)的模块的名字相当于是
23 //告诉顶层我要使用来自 half_adder 这个模块的功能
24 //后面的是在顶层中重新起的在本模块中的名字相当
25 //于是这个模块具体到顶层中
26 (
27 .in1 (in1 ), //input in1 前面 in1 是相当于 half_adder 模块中的信号
28 //(in1)顶层中的信号,然后最前面加上“.”,可以形象的
29 //理解为把这两个信号线连接到一起(rtl 中的实例化过程和
30 //Testbench 中的实例化过程是一样的,可以对比理解学习)
31
32 .in2 (in2 ), //input in2
33
34 .sum (h0_sum ), //ouptut sum
35 .cout (h0_cout) //output cout
36 );
37
38 //------------------------half_adder_inst1------------------------
39 half_adder half_adder_inst1//同一个模块可以被实例化多次(所以相同功能只设计
40 //一个通用模块即可),但是在顶层的名字一定要区别开,
41 //这样子才能表达出是实例化的两个相同功能的模块
42 (
43 .in1 (h0_sum ), //input in1
44 .in2 (cin ), //input in2
45
46 .sum (sum ), //ouptut sum
47 .cout (h1_cout) //output cout
48 );
49
50 //cout:总的进位信号
51 assign cout = h0_cout | h1_cout;
52
53 endmodule
顶层代码编写完成后还不能直接综合,否则会有如图 所示的报错提示,这是提示,你找不到 half_adder 这个模块,也就是说你虽然实例化,但是还没有把 half_adder 这个模块添加到工程中,所以识别不了。
所以我们还要将半加器的.v 文件拷贝到全加器的 rtl 文件夹
中,如图所示,然后再将该文件添加到工程中。把文件添加到工程中的过程和添加Testbench 文件的方式是一样的,找准文件夹的位置,按照相同的步骤添加即可。
10.2 仿真文件
1 `timescale 1ns/1ns
2 module tb_full_adder();
3
4 reg in1;
5 reg in2;
6 reg cin;
7
8 wire sum ;
9 wire cout;
10
11 //初始化输入信号
12 initial begin
13 in1 <= 1'b0;
14 in2 <= 1'b0;
15 cin <= 1'b0;
16 end
17
18 //in1:产生输入随机数,模拟加数 1 的输入情况
19 //取模求余数,产生随机数 1'b0、1'b1,每隔 10ns 产生一次随机数
20 always #10 in1 <= {$random} % 2;
21
22 //in2:产生输入随机数,模拟加数 2 的输入情况
23 always #10 in2 <= {$random} % 2;
24
25 //cin:产生输入随机数,模拟前级进位的输入情况
26 always #10 cin <= {$random} % 2;
27
28 //------------------------------------------------------------
29 initial begin
30 $timeformat(-9, 0, "ns", 6);
31 $monitor("@time %t:in1=%b in2=%b cin=%b sum=%b cout=%b",
32 $time,in1,in2,cin,sum,cout);
33 end
34 //------------------------------------------------------------
35
36 //---------------full_adder_inst------------------
37 full_adder full_adder_inst(
38 .in1 (in1 ), //input in1
39 .in2 (in2 ), //input in2
40 .cin (cin ), //input cin
41
42 .sum (sum ), //output sum
43 .cout (cout ) //output cout
44 );
45
46 endmodule