赞
踩
通过对杜老师新手代码的抄写,并对学生实验进行验证,练习和体会到了Verilog语言常用的语法,同时,结合夏宇闻老师的《Verilog数字系统设计教程》,对Verilog语言以及RTL电路有了初步的认识。
module duoluxuanzheqi
(
IN0,
IN1,
IN2,
IN3,
SEL1,
SEL2,
OUT
);
parameter WL = 16;//输入输出数据信号位宽
input [WL-1 : 0] IN0, IN1, IN2, IN3;
input SEL1, SEL2;
output [WL-1 : 0] OUT;
reg [WL-1 : 0] OUT;
// 生成组合逻辑的代码
always @ (IN0 or IN1 or IN2 or IN3 or SEL1 or SEL2)
// begin
// if(SEL1 && SEL2) //11
// OUT = IN3;
// else if((SEL1 == 0) && (SEL2 == 0))//00
// OUT = IN0;
// else if((SEL1 == 0) && (SEL2 == 1))//01
// OUT = IN1;
// else if((SEL1 == 1) && (SEL2 == 0))//10
// OUT = IN2;
// end
begin
case ({SEL1, SEL2})
2'b00: OUT = IN0;
2'b01: OUT = IN1;
2'b10: OUT = IN2;
2'b11: OUT = IN3;
default: OUT = 1'bx;
endcase
end
endmodule
//4选1的mux实际上就是在2选1的mux上进行拓展
module jiaochakaiguan
(
IN0,
IN1,
IN2,
IN3,
SEL0,
SEL1,
SEL2,
SEL3,
OUT0,
OUT1,
);
parameter WL = 16;
input [WL-1 : 0] IN0, IN1, IN2, IN3;
input SEL1, SEL0, SEL2, SEL3;
output [WL-1 : 0] OUT0, OUT1;
reg [WL-1 : 0] OUT0, OUT1;
always @ (IN0 or IN1 or IN2 or IN3 or SEL0 or SEL1)
begin
case ({SEL0, SEL1})
2'b00 : OUT0 = IN0;
2'b01 : OUT0 = IN1;
2'b10 : OUT0 = IN2;
2'b11 : OUT0 = IN3;
endcase
end
always @ (IN0 or IN1 or IN2 or IN3 or SEL2 or SEL3)
begin
case ({SEL2, SEL3})
2'b00 : OUT1 = IN0;
2'b01 : OUT1 = IN1;
2'b10 : OUT1 = IN2;
2'b11 : OUT1 = IN3;
endcase
end
endmodule
//module jiaochakaiguan
//(
// IN0,
// IN1,
// SEL0,
// SEL1,
// OUT0,
// OUT1
//);
// parameter WL = 16;
// input [WL-1 : 0] IN0, IN1;
// input SEL1, SEL0;
// output [WL-1 : 0] OUT0, OUT1;
//
// reg [WL-1 : 0] OUT0, OUT1;
//
// always @ (IN0 or IN1 or SEL0)
always @ (IN0 or IN1 or OUT0 or OUT1)
// begin
// if(SEL0)
// OUT0 = IN1;
// else
// OUT0 = IN0;
// end
//
// always @ (IN0 or IN1 or SEL1)
// begin
// if(SEL1)
// OUT1 = IN1;
// else
// OUT1 = IN0;
// end
//endmodule
module youxianbianmaqi
(
IN,
OUT
);
input [3 : 0] IN;
output [2 : 0] OUT;
reg [2 : 0] OUT ;
always @ (IN)//检测输入活动
begin
if(IN[3])
OUT = 3'b011;
else if(IN[2])
OUT = 3'b010;
else if(IN[1])
OUT = 3'b001;
else if(IN[0])
OUT = 3'b000;
else
OUT = 3'b111;
end
endmodule
module duoluyimaqi
(
IN,
OUT
);
input [3:0] IN;
output [15:0] OUT;
reg [15:0] OUT;
always @ (IN)
begin
case ({IN})
4'b0000: OUT = 16'b0000_0000_0000_0001;
4'b0001: OUT = 16'b0000_0000_0000_0010;
4'b0010: OUT = 16'b0000_0000_0000_0100;
4'b0011: OUT = 16'b0000_0000_0000_1000;
4'b0100: OUT = 16'b0000_0000_0001_0000;
4'b0101: OUT = 16'b0000_0000_0010_0000;
4'b0110: OUT = 16'b0000_0000_0100_0000;
4'b0111: OUT = 16'b0000_0000_1000_0000;
4'b1000: OUT = 16'b0000_0001_0000_0000;
4'b1001: OUT = 16'b0000_0010_0000_0000;
4'b1010: OUT = 16'b0000_0100_0000_0000;
4'b1011: OUT = 16'b0000_1000_0000_0000;
4'b1100: OUT = 16'b0001_0000_0000_0000;
4'b1101: OUT = 16'b0010_0000_0000_0000;
4'b1110: OUT = 16'b0100_0000_0000_0000;
4'b1111: OUT = 16'b1000_0000_0000_0000;
//default
endcase//全的case,所以不需要写default
end
endmodule
//module duoluyimaqi
//(
// IN,
// OUT
//);
//
// input [2:0] IN;
// output [7:0] OUT;
//
// reg [7:0] OUT;
//
// always @ (IN)
// begin
// case ({IN})
// 3'b000: OUT = 8'b0000_0001;
// 3'b001: OUT = 8'b0000_0010;
// 3'b010: OUT = 8'b0000_0100;
// 3'b011: OUT = 8'b0000_1000;
// 3'b100: OUT = 8'b0001_0000;
// 3'b101: OUT = 8'b0010_0000;
// 3'b110: OUT = 8'b0100_0000;
// 3'b111: OUT = 8'b1000_0000;
// //default
// endcase//全的case,所以不需要写default
// end
// endmodule
//
加法器根据不同的功能需求有着不同的代码实现方式,尤其要注意带流水线的加法器引入D触发器,使用D触发器把大块的组合逻辑分割为小块,最终让电路获得更高的时钟频率。
module wufuhaojiafaqi
(
IN1,
IN2,
OUT
);
inout [3 : 0] IN1, IN2;
output [4 : 0] OUT;
reg [4 : 0] OUT;
always @ (IN1 or IN2)
begin
OUT = IN1+IN2;
end
endmodule
module bumajiafaqi
(
IN1,
IN2,
OUT
);
input signed [3:0] IN1, IN2;
output signed [4:0] OUT;
reg signed [4:0] OUT;
always @(IN1 or IN2)
begin
OUT = IN1+IN2;
end
endmodule
//纯粹的加法器是一堆组合逻辑门构成的,这些组合逻辑的计算延迟较大,
//如果加法器电路的前极或后级电路也是一个规模较大的组合逻辑,
//那么它们会和加法器电路合并成为一个更大的组合逻辑,从而带来更大的组合逻辑计算延迟。
//每一个D触发器都有其所容许的最小的建立与保持时间,
//当两个D触发器之间的组合电路逻辑延迟变得更大的时候,
//会导致电路只能工作在更低的时钟频率,为了让电路能够工作在更高的时钟频率,
//需要用D触发器来把大块的组合逻辑分割为小块,这就是流水线技术。(建议自行Google 关键字 D触发器 建立与保持时间)
module daliusuixiandejiafaqi
(
IN1,
IN2,
CLK,
OUT
);
inout [3: 0] IN1, IN2;
input CLK;
output [4: 0] OUT ;
reg [3: 0] in1_d1R, in2_d2R;//加到d触发器输入的输入 连接两个组合逻辑啊电路的输出
reg [4: 0] adder_out, OUT;//adder_out加到d触发器的输出
always @(posedge CLK)//生成D触发器的always块
begin
in1_d1R <= IN1;//赋值
in2_d2R <= IN2;
OUT <= adder_out;
end
always @(in1_d1R or in2_d2R)//生成组合逻辑的always块
begin
adder_out = in1_d1R + in2_d2R;
end
endmodule
带流水线的加法器D触发器在RTL电路中的体现
//无符号的乘法器
module chengfaqi
(
IN1,
IN2,
OUT
);
input [3: 0] IN1, IN2;
output [7: 0] OUT;
reg [7: 0] OUT;
always @(IN1 or IN2)
begin
OUT = IN1 * IN2;
end
endmodule
//有符号的乘法器
module
(
IN1,
IN2,
OUT
);
input signed[3:0] IN1, IN2;
output signed [7:0] OUT;
reg signed[7:0] OUT;
always@(IN1 or IN2)
begin // 生成组合逻辑的always 块
OUT = IN1 * IN2;
end
endmodule
乘法比较浪费资源,有些情况下可以用加法来进行代替
例如:
A3 = A2 + A = (A << 1) + A
//8wei mul
module chengfaqi
(
IN1,
IN2,
OUT
);
input signed [7 : 0] IN1, IN2;
output signed [15 : 0] OUT;
reg signed [15 : 0] OUT;
always @ (IN1 or IN2)
begin
OUT = IN1 * IN2;
end
endmodule
通过比较仿真波形,可以明显看出十六位乘法器的输出延迟要比八位乘法器延迟大,并且仿真时间也更长
//8wei D mul
module chengfaqi
(
IN1,
IN2,
CLK,
OUT
);
input signed [7 : 0] IN1, IN2;
input CLK;
output signed [15 : 0] OUT;
reg signed [15 : 0] OUT, mul_out;
reg signed [7 : 0] in1_d1R, in2_d1R;
always @ (posedge CLK)
begin
in1_d1R <= IN1;
in2_d1R <= IN2;
OUT <= mul_out;
end
always @ (in1_d1R or in2_d1R)
begin
mul_out = in1_d1R * in2_d1R;
end
endmodule
RTL电路
通过比较八位带D触发器与不带D触发器的波形仿真图,可以观察到毛刺减少。
module jishuqi
(
RST,//异步复位,高有效
CLK,//时钟,上升沿有效
EN,//输入的计数使能,高有效
CLR,//输入的清零信号,高有效
LOAD,//输入的数据加载使能信号,高有效
DATA,//输入的加载数据信号
CNTVAL,//输出的计数值信号
OV//计数溢出信号,计数值为最大值时该信号为1
);
input RST, CLK, EN, CLR, LOAD;
input [3: 0] DATA;
output [3: 0] CNTVAL;
output OV;
reg [3: 0] CNTVAL, cnt_next;
reg OV;
//电路编译参数,最大计数值
parameter CNT_MAX_VAL = 9;
//组合逻辑,生成cnt_next
//计数使能最优先,清零第二优先,加载第三优先
always@(EN or CLR or LOAD or DATA or CNTVAL)
begin
if(EN)//使能有效
begin
if(CLR)//清零有效
begin
cnt_next = 0;
end
else//清零无效
begin
if(LOAD)//加载有效
begin
cnt_next = DATA;
end
else//加载无效,正常计数
//使能有效,清零和加载都无效,根据当前计数值计算下一值
begin
if(CNTVAL < CNT_MAX_VAL)//未溢出最大值,下一值加一
begin
cnt_next = CNTVAL + 1'b1;
end
else//计数值达到最大,溢出后cnt_next归零
begin
cnt_next = 0;
end
end//else LOAD
end//else CLR
end//if EN
else//使能无效,计数值保持不变
begin
cnt_next = CNTVAL;
end
end
//时序逻辑 更新下一时钟周期的计数值
//CNTVAL 会被编译为D触发器
always @(posedge CLK or posedge RST)//上升沿触发
begin
if(RST)
CNTVAL <= 0;
else
CNTVAL <= cnt_next;
end
//组合逻辑,生成ov
always @(CNTVAL)
begin
if(CNTVAL == CNT_MAX_VAL)
OV = 1;
else
OV = 0;
end
endmodule
//有限状态机
//把要解决的问题映射到状态空间,包括:
//定义哪些状态
//状态之间的跳转逻辑是怎样的
//使用表格和状态图来描述状态机的跳转逻辑
//使用parameter定义状态
//尽量使用三段式的标准写法
//本例中,我们要实现一个可乐售卖机的状态机电路,假设可乐3分钱1罐,机器只接受1分钱的硬币,当投入第三个硬币的时候,投出一罐可乐
//Tools-Netlist Viewers-State Machine Viewer查看状态机的状态转移图和表达式
module sanduanshizhuangtaijidaima
(
CLK, //clock
RST, //
CENT1IN, //input 1 cent coin
TINOUT //output 1 tin cola
);
input CLK;
input RST;
input CENT1IN; //输入硬币
output TINOUT;
parameter ST_0_CENT = 0;//硬币数量
parameter ST_1_CENT = 1;
parameter ST_2_CENT = 2;
parameter ST_3_CENT = 3;
reg [2-1 : 0] stateR;
reg [2-1 : 0] next_state;
reg TINOUT;
//calc next state
always @ (CENT1IN or stateR)//检测
begin
case (stateR)
ST_0_CENT : //检测到硬币数量为0
begin
if(CENT1IN)//如果继续投硬币,next_state增加一
next_state = ST_1_CENT;
else
next_state = ST_0_CENT;
end
ST_1_CENT : //检测到硬币数量为1
begin
if(CENT1IN)
next_state = ST_2_CENT;
else
next_state = ST_1_CENT;
end
ST_2_CENT :
begin
if(CENT1IN)
next_state = ST_3_CENT;
else
next_state = ST_2_CENT;
end
ST_3_CENT :
begin
next_state = ST_0_CENT;
end
endcase
end
//state DEF 每个周期进行一次计算,把next_state的值传递给stateR,clk周期很快
always @ (posedge CLK or posedge RST)
begin
if(RST)
stateR <= ST_0_CENT;//复位
else
stateR <= next_state;
end
//???调换顺序?????
//calc output
always @ (stateR)//判断stateR是否满足输出条件
begin
if(stateR == ST_3_CENT)
TINOUT = 1'b1;
else
TINOUT = 1'b0;
end
endmodule
基本要求:
电路每个时钟周期输入1比特数据,当捕获到1011的时钟周期,电路输出1,否则输出0
使用序列101011010作为输出的测试序列
扩展要求:
给你的电路添加输入使能端口,只有输入使能EN为1的时钟周期,才从输入的数据端口向内部获取1比特序列数据。
状态转移图
module zhuangtaiji(
CLK, // clock
RST, // reset
CENT1IN, // input 1 cent coin
TINOUT,
EN
); // output 1 tin cola
input CLK;
input RST, EN;
input CENT1IN;
output TINOUT;
parameter ST_0_CENT = 0;
parameter ST_1_CENT = 1;
parameter ST_2_CENT = 2;
parameter ST_3_CENT = 3;
parameter ST_4_CENT = 4;
reg [3:0] stateR;
reg [3:0] next_state;
reg TINOUT;
always @ (CENT1IN or stateR)
begin
case (stateR)
ST_0_CENT :begin if (CENT1IN && EN) next_state = ST_1_CENT ; else next_state = ST_0_CENT; end
ST_1_CENT :begin if ((CENT1IN == 0) && EN) next_state = ST_2_CENT ; else next_state = ST_1_CENT; end
ST_2_CENT :begin if (CENT1IN && EN) next_state = ST_3_CENT ; else next_state = ST_0_CENT; end
ST_3_CENT :begin if (CENT1IN && EN) next_state = ST_4_CENT ; else next_state = ST_2_CENT; end
ST_4_CENT :begin if (CENT1IN && EN) next_state = ST_1_CENT ; else next_state = ST_2_CENT; end
default next_state = ST_0_CENT;
endcase
end
// calc output
always @ (stateR)
begin
if(stateR == ST_4_CENT)
TINOUT = 1'b1;
else
TINOUT = 1'b0;
end
// state DFF
always @ (posedge CLK or posedge RST)
begin
if(RST)
stateR <= ST_0_CENT;
else
stateR <= next_state;
end
endmodule
能够识别“1011”序列的状态机仿真波形
//串行数据和并行数据之间的相互转换是在接口设计中很常见的功能,
//一般而言,数据在FPGA内部都是并行传递的,
//当通过串行接口协议(例如SPI,I2C,I2S等)把数据从FPGA内部传送到一个外部芯片
//(例如一片EEPROM存储器或是一片音频DAC)时就需要用到串并转换了,其核心的电路是移位寄存器。
module yiweijicunqi
(
RST, //异步复位,高有效
CLK, //时钟,上升沿有效
EN, //输入数据串行移位使能
IN, //输入串行数据
OUT //并行输出数据
);
input RST, CLK, EN;
input IN;
output [3 : 0] OUT;
reg [3 : 0] shift_R;
assign OUT [3 : 0] = shift_R[3 : 0];
//时序逻辑,根据输入使能进行串行移位
// shift_R 会被编译为D触发器
always @ (posedge CLK or posedge RST) //时钟,上升沿有效 异步复位,高有效 posedge从低到高 从高到低 都会触发
begin
if(RST) //异步复位,高有效
shift_R[3 : 0] <= 0;//清零
else if(EN) //异步复位,高有效
begin // 串行移位的使能有效
shift_R[3 : 1] <= shift_R[2 : 0];
shift_R[0] <= IN;
end
else
begin // 使能无效保持不动
shift_R[3 : 0] <= shift_R[3 : 0];
end
end // always
endmodule
module dpram
(
WE,
WCLK,
RCLK,
WA,
RA,
WD,
RD
);
parameter DATAWL = 0;
parameter ADDRWL = 0;
parameter C2Q = 0;
input WE, WCLK, RCLK;
input [ADDRWL-1 : 0] WA, RA;
input [DATAWL-1 : 0] WD;
output [DATAWL-1 : 0] RD;
reg [DATAWL-1 : 0] RD;
reg [DATAWL-1 : 0] mem [(1<<ADDRWL)-1 : 0]; ///????
always @ (posedge WCLK)
begin
if(WE)
mem[WA] <= #C2Q WD;//????
end
always @ (posedge RCLK)
begin
RD <= #C2Q mem[RA];
end
// ######################################
// synopsys translate_off
// ######################################
// the code below this line will NOT take part into synthesis
// they are only needed by RTL simulation
// task DumpDpRAM, get the content of RAM[addr]
task DumpDpRAM;
input [ADDRWL-1 : 0] addr;
input [DATAWL-1 : 0] content;
begin
content = mem[addr];
end
endtask
task RAMInit;
integer i;
reg [DATAWL-1 : 0] initData;
begin
initData = ‘hAAAA;
//initData = (1<<DATAWL) - 1;
for(i = 0; i << (1 << ADDRWL); i = i + 1)
mem[i] = initData;
end
endtask
initial
begin
RAMInit();
$display("module dpram().RAMInit()called @ %0d", $time);
end
// ######################################
// synopsys translate_on
// ######################################
// the code below this line will take part in synthesis
endmodule
module ROM
(
CLK,
RA,
RD
);
input CLK;
input [6 : 0] RA;
output [12 : 0] RD;
reg [12 : 0] RD;
always @ (posedge CLK)
case(RA)
7'd0 : RD = #1 13'd 0;
7'd1 : RD = #1 13'd 101;
7'd2 : RD = #1 13'd 201;
7'd3 : RD = #1 13'd 301;
... ... ...
7'd127 : RD = #1 13'd 8190;
endcase
endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。