计算机系统(PC机)系统配置的基本需求:CPU: Intel奔腾系列,或AMD Athlon/XP;操作系统:Windowns NT/2000/XP;内存:256M或以上;显卡:支持256色的8位显卡或以上;硬盘:20G以上。Altera® Quartus® II 设计软件提供完整的多平台设计环境,它可以轻易满足特定设计的需要。它是可编程片上系统 (SOPC) 设计的综合性环境。QuartusII 软件拥有 FPGA 和 CPLD 设计的所有阶段的解决方案。

4、简单时序逻辑设计在Verilog HDL中,相对于组合逻辑电路,时序逻辑电路也有规定的表述方式。在可综合的Verilog HDL模型,我们通常使用always块和 @(posedge clk)或 @(negedge clk)的结构来表述时序逻辑。
5、时序逻辑设计示例:可综合的1/2分频器模型。
6、// half_clk.v:
7、module half_clk(reset,clk_in,clk_out);
8、input clk_in,reset;
9、output clk_out;
10、reg clk_out;
11、always @(posedge clk_in)
12、begin
13、if(!reset) clk_out=0;
14、else
15、clk_out=~clk_out;
16、end
17、endmodule
18、在always块中,被赋值的信号都必须定义为reg型,这是由时序逻辑电路的特点所决定的。对于reg型数据,如果未对它进行赋值,仿真工具会认为它是不定态。为了能正确地观察到仿真结果,在可综合风格的模块中我们通常定义一个复位信号reset,当reset为低电平时,对电路中的寄存器进行复位。
19、仿真波形:

4、设计时序逻辑时采用阻塞赋值与非阻塞赋值的区别非阻塞赋值语句(<=)右端表达式计算完后并不立即赋值给左端,而是同时启动下一条语句继续执行,而阻塞赋值语句(=)在每个右端表达式计算完成后立即赋值给左端变量。非阻塞赋值不能用于assign语句中,而只能用于对寄存器型变量进行赋值,只能在intial和always等过程过程块中。阻塞赋值既能用于assign语句,也能用于intial和always等过程赋值中。对于时序逻辑的描述和建模,应尽量使用非阻塞赋值方式。若在同一个always过程块中描述时序和组合逻辑混合电路时也最好使用非阻塞赋值方式。在always块中,阻塞赋值可以理解为赋值语句是顺序执行的,而非阻塞赋值可以理解为赋值语句是并发执行的。实际的时序逻辑设计中,一般的情况下非阻塞赋值语句被更多地使用,有时为了在同一周期实现相互关联的操作,也使用了阻塞赋值语句。可综合设计中阻塞赋值和非阻塞赋值设计要点:1)非阻塞赋值不能用于assign持续赋值中,一般只出现在initial和always等过程块中,对reg型变量进行赋值;2)当用always块来描述组合逻辑时,既可以用阻塞赋值,也可以用非阻塞赋值。但在同一个过程块中,最好不要同时用阻塞赋值和非阻塞赋值;3)在向函数funtion的返回值赋值时,应使用阻塞赋值;
5、4)不能在一个以上的always过程块中对同一个变量赋值,这样会引起冲突,在综合时会报错;
6、5)在一个模块中,严禁对同一个变量既进行阻塞赋值,又进行非阻塞赋值,这样会在综合时报错;
7、6)对时序逻辑的描述和建模,应尽量使用非阻塞赋值方式,此外,若在同一个always过程块中描述时序和组合逻辑混合电路时,最好使用非阻塞赋值方式。
8、阻塞赋值与非阻塞赋值示例:分别采用阻塞赋值语句和非阻塞赋值语句的两个看上去非常相似的两个模块blocking.v和non_blocking.v来阐明两者之间的区别。
9、模块源代码:
10、// ------------- blocking.v ---------------
11、module blocking(clk,a,b,c);
12、output [3:0] b,c;
13、input [3:0] a;
14、input clk;
15、reg [3:0] b,c;
16、always @(posedge clk)
17、begin
18、b = a;
19、c = b;
20、$display("Blocking: a = %d, b = %d, c = %d.",a,b,c);
21、end
22、endmodule
23、仿真波形(部分):

函数和任务的应用设计
1、实验目的1、掌握函数在模块设计中的使用;2、掌握任务在结构化Verilog HDL设计中的应用。
2、函数在模块设计中的使用函数可以在模块不同位置执行共同代码。函数只能返回一个值,它不能包含任何时延或时序控制(必须立即执行),并且它不能调用其它的任务。函数必须带有至少一个输入,在函数中允许没有输出或输入输出说明。函数可以调用其它的函数。函数说明部分可以在模块说明中的任何位置出现,函数的输入是由输入说明指定。如果函数说明部分中没有指定函数取值范围,则其缺省的函数值为1位二进制数。函数定义在函数内部隐式地声明一个寄存器变量,该寄存器变量与函数同名并且取值范围相同。函数通过在函数定义中显式地对该寄存器赋值来返回函数值。对这一寄存器的赋值必须出现在函数定义中。函数调用是表达式的一部分。与任务相似,函数定义中声明的所有局部寄存器都是静态的,即函数中的局部寄存器在函数的多个调用之间保持它们的值。下例是函数调用的一个简单示范,采用同步时钟触发运算的执行,每个clk时钟周期都会执行一次运算。并且在测试模块中,通过调用系统任务$display在时钟的下降沿显示每次计算的结果。模块源代码:module tryfunct(clk,result,reset);output[31:0] result;input reset,clk;reg[31:0] result;always @(posedge clk) //clk的上沿触发同步运算。beginif(!reset) //reset为低时复位。result<=0;elsebeginresult <= factorial(3);endendfunction [31:0] factorial; //函数定义。input [3:0] operand;reg [3:0] index;beginfactorial = operand ? 1 : 0;for(index = 2; index <= operand; index = index + 1)factorial = index * factorial;endendfunctionendmodule上例中函数factorial(n)实际上就是阶乘运算。必须提醒大家注意的是,在实际的设计中,我们不希望设计中的运算过于复杂,以免在综合后带来不可预测的后果。经常的情况是,我们把复杂的运算分成几个步骤,分别在不同的时钟周期完成。
3、任务在结构化设计中的应用一个任务就像一个过程,它可以从描述的不同位置执行共同的代码段。共同的代码段用任务定义编写成任务,这样它就能够从设计描述的不同位置通过任务调用被调用。任务可以包含时序控制,即时延控制,并且任务也能调用其它任务和函数。任务可以没有或有一个或多个参数。值通过参数传入和传出任务。除输入参数外(参数从任务中接收值),任务还能带有输出参数(从任务中返回值)和输入输出参数。任务的定义在模块说明部分中编写。任务调用: 一个任务由任务调用语句调用。任务调用语句给出传入任务的参数值和接收结果的变量值;
4、 任务调用语句是过程性语句,可以在always 语句或initial 语句中使用;
5、 任务调用语句中参数列表必须与任务定义中的输入、输出和输入输出参数说明的顺序匹配。此外,参数要按值传递,不能按地址传递;
6、 任务能够包含定时控制,任务可在被调用后再经过一定时延才返回值;
7、 任务调用语句是过程性语句,所以任务调用中的输出和输入输出参数必须是寄存器类型的;
8、 任务可以带有时序控制,或等待特定事件的发生。但是,输出参数的值直到任务退出时才传递给调用参数。
9、实例应用:利用task和电平敏感的always块设计比较后重组信号的组合逻辑。可以看到,利用task非常方便地实现了数据之间的交换,如果要用函数实现相同的功能是非常复杂的;另外,task也避免了直接用一般语句来描述所引起的不易理解和综合时产生冗余逻辑等问题。
10、模块源代码:
11、//----------------- sort4.v ------------------
12、module sort4(ra,rb,rc,rd,a,b,c,d);
13、output[3:0] ra,rb,rc,rd;
14、input[3:0] a,b,c,d;
15、reg[3:0] ra,rb,rc,rd;
16、reg[3:0] va,vb,vc,vd;
17、always @ (a or b or c or d)
18、begin
19、{va,vb,vc,vd}={a,b,c,d};
20、sort2(va,vc); //va 与vc互换。
21、sort2(vb,vd); //vb 与vd互换。
22、sort2(va,vb); //va 与vb互换。
23、sort2(vc,vd); //vc 与vd互换。
24、sort2(vb,vc); //vb 与vc互换。
25、{ra,rb,rc,rd}={va,vb,vc,vd};
26、end
27、task sort2;
28、inout[3:0] x,y;
29、reg[3:0] tmp;
30、if(x>y)
31、begin
32、tmp=x; //x与y变量的内容互换,要求顺序执行,所以采用阻塞赋值方式。
33、x=y;
34、y=tmp;
35、end
36、endtask
37、endmodule
38、值得注意的是task中的变量定义与模块中的变量定义不尽相同,它们并不受输入输出类型的限制。如此例,x与y对于task sort2来说虽然是inout型,但实际上它们对应的是always块中变量,都是reg型变量。
39、仿真波形(部分):

基于模块多层次引用的结构化电路设计
1、实验目的1、了解复杂电路与系统的“top-down”设计思想;2、掌握简单多层次电路的描述方法。
2、多层次结构电路的设计复杂数字系统可采用“top-down”的方法进行设计:首先把系统分为几个模块,每个模块在分为几个子模块,依次类推,知道易于实现为止。这种“top-down”的方法能够把复杂的设计分为许多简单的设计来实现,同时也适合于多人进行合作开发。多层次结构电路的描述既可以采用文本方式,也可以采用图形和文本混合设计的方式。被调用模块的指定方式:1)文件复制方式;2)使用`include语句;3)库管理方式。
3、设计实例分析这个实例的功能是将并行数据转化为串行数据送交外部电路编码,并将解码后得到的串行数据转化为并行数据交由CPU处理。显而易见,这实际上是两个独立的逻辑功能,分别设计为独立的模块,然后再合并为一个模块显得目的明确、层次清晰。模块源代码// ---------------- p_to_s.v ---------------------------------module p_to_s(D_in,T0,data,SEND,ESC,ADD_100);output D_in,T0; // D_in是串行输出,T0是移位时钟并给// CPU中断,以确定何时给出下个数据。input [7:0] data; //并行输入的数据。input SEND,ESC,ADD_100; //SEND、ESC共同决定是否进行并到串//的数据转化。ADD_100决定何时置数。wire D_in,T0;reg [7:0] DATA_Q,DATA_Q_buf;assign T0 = ! (SEND & ESC); //形成移位时钟。.assign D_in = DATA_Q[7]; //给出串行数据。always @(posedge T0 or negedge ADD_100) //ADD_100下沿置数,T0上沿移位。beginif(!ADD_100)DATA_Q = data;elsebeginDATA_Q_buf = DATA_Q<<1; //DATA_Q_buf作为中介,以令综合器DATA_Q = DATA_Q_buf; //能辨明。endendendmodule仿真波形:在p_to_s.v中,由于移位运算虽然可综合,但是不是简单的RTL级描述,直接用DATA_Q<=DATA_Q<<1的写法在综合时会令综合器产生误解。另外,在该设计中,由于时钟T0的频率较低,所以没有象以往那样采用低电平置数,而是采用ADD_100的下降沿置数。//--------------------- s_to_p.v ---------------------------

26、将上面的两个模块合并起来的sys.v的源代码:
27、//------------------- sys.v ---------------------------
28、`include "./p_to_s.v"
29、`include "./s_to_p.v"
30、module sys(D_in,T0,T1, data, D_out,SEND,ESC,DSC,TAKE,ADD_100,ADD_101);
31、input D_out,SEND,ESC,DSC,TAKE,ADD_100,ADD_101;
32、inout [7:0] data;
33、output D_in,T0,T1;
34、p_to_s p_to_s(.D_in(D_in),.T0(T0),.data(data),.SEND(SEND),.ESC(ESC),.ADD_100(ADD_100));
35、s_to_p s_to_p(.T1(T1),.data(data),.D_out(D_out),.DSC(DSC),.TAKE(TAKE),.ADD_101(ADD_101));
36、endmodule
37、仿真波形:
