使用Quartus和ModelSim

我一直对数字电路特别是硬件描述语言-HDL感兴趣。长期以来,我在未来的阅读清单中有一本David M. Harris和Sarah L. Harris所著的《数字电路与计算机体系结构》一书,利用空闲时间进行自我隔离,我读到了这本精彩的书。在阅读过程中,我遇到了一些困难,尤其是如何在Quartus Prime中编写和调试代码。在搜索过程中,marsohod.org网站确实为我提供了帮助,但是此站点上的电路仿真过程是使用Quartus内置工具进行描述的,并且在程序的现代版本中,没有此类内置工具,因此您需要使用ModelSim。为了以某种方式系统化我从Quartus和ModelSim获得的知识,我决定写这篇文章。在本文的过程中,我将以David M. Harris和Sarah L. Harris所著的“计算机的数字电路和体系结构”一书中的任务为例,特别是有关苏打水的任务3.26。在整篇文章中,我将向您展示如何安装Quartus,创建项目,编写代码并对其进行仿真。对这个感兴趣的每个人都欢迎。



图片



问题的提法



您被说服为办公室设计一种茶点机。饮料部分由工会承保,因此仅售5卢布。机器接受1、2和5卢布的硬币。客户支付所需金额后,咖啡机将立即分配饮料并退还零钱。设计软饮料机的状态机。机器的输入是1、2和5卢布,即插入这些硬币中的哪一个。



假设对于每个时钟信号,仅插入一个硬币。机器的输出:倒苏打水,返还1卢布,返还2卢布,返还2换2卢布。一旦在机器中收集了5卢布(或更多),它将设置信号“ POUR GASING”,以及返回相应变化的信号。然后,机器必须准备好再次接受硬币。



理论



有限状态机或有限状态机(FSM)属于代表大多数数字电路的同步时序电路。这就是您应该如何实施项目的方法(至少在最初是这样)。该方法提供了电路的可重复性和验证,并且独立于各种电路元件的延迟关系。构造同步时序电路的规则规定,如果电路的元素满足以下条件,则该电路为同步时序电路:



  • 每个电路元件可以是寄存器或组合电路。
  • 至少一个模式元素是一个寄存器。
  • 所有寄存器均由单个时钟信号提供时钟。
  • 每个循环路径至少包含一个寄存器。


状态机具有几种状态,并将其存储在寄存器中。当时钟信号到达时,状态机可以更改其状态,并且状态将如何准确更改取决于输入信号和当前状态。在最简单的情况下,可能根本没有任何输入信号,因此分频器可以工作。有限状态机主要有两类:Moore自动机,其中输出信号仅取决于自动机的当前状态;和Mealy自动机,其中输出信号取决于当前状态和输入信号。原则上,任何有限状态机都可以根据Moore方案和Miley方案来实现,它们之间的区别在于Moore自动机将具有更多状态,并且将比Mily自动机落后一个时钟。对于纯碱机电路,我将使用Miles电路。让我们说明一下状态机:

符号 描述
S 0 初始状态下,累计金额为0卢布。
小号1 累计金额为1卢布。
小号2 累计2卢布。
小号3 累计3卢布。
小号4 累计4卢布。


输入信号将是一个两位总线,并带有以下硬币面额编码:

符号 描述
1 01 1擦
2 2卢布
5 十一 5卢布


让我们绘制一个自动机的状态图(在Mealy自动机的状态图上,有必要在状态转换箭头上指示输出信号,我不会这样做,以免弄乱该图,所有输出信号将在下表中描述):



图片



让我们写下状态和输出信号的变化表:

状态 输入信号
S S' insert pour_water C 1 . change1 2 . change2 2 2 . change22
S0 S1 I1 0 0 0 0
S0 S2 I2 0 0 0 0
S0 S0 I5 1 0 0 0
S1 S2 I1 0 0 0 0
S1 S3 I2 0 0 0 0
S1 S0 I5 1 1 0 0
S2 S3 I1 0 0 0 0
S2 S4 I2 0 0 0 0
S2 S0 I5 1 0 1 0
S3 S4 I1 0 0 0 0
S3 S0 I2 1 0 0 0
S3 S0 I5 1 1 1 0
S4 S0 I1 1 0 0 0
S4 S0 I2 1 1 0 0
S4 S0 I5 1 0 0 1




Quartus Prime



Quartus有一个免费的精简版,与专业版相比有一些限制,主要限制是用于项目仿真的源代码不超过10,000行。下载它,注册后,您可以单击链接,在编写最新版本为19.1的时候,我根据使用该版本的知识撰写了一篇文章。我们选择精简版19.1版Windows操作系统(应注意的是,有一个Quartus for Linux版本,它运行良好,ModelSim出现了问题,该版本为32位,并使用了旧版本的字体显示库,因此首先建议使用Windows版本),选择“合并文件”标签。下载的存档大小非常大-5.6 Gb,请记住这一点。我们扩展下载的档案并运行setup.bat安装以标准方式进行,我们使用默认的组件选择。



项目创建



要创建一个新项目,请选择File-> New ... by Project Wizard。第一个向导窗口是信息性窗口,单击“ 下一步”,在第二个窗口中,选择项目所在的位置,其名称为“ soda_machine”,以及顶层设计元素“ soda_machine”,如图所示:



图片



在下一个窗口中,选择“ Empty project”。用于添加文件的窗口“添加文件”,不添加任何内容。选择“ Family,Devices&Board Settings”设备的窗口对于真实项目非常重要,但是由于我们的项目远非真实,因此在此处保留默认设置,如图所示:



图片



选择其他工具设置的窗口在“ EDA工具设置”中,选择要模拟的项目以使用“ ModelSim-Altera”“系统Verilog HDL”格式,如图所示:



图片



最后一个信息窗口“摘要”,单击“ 完成”



编写源代码



我们将有两个带有源代码的主文件,这是soda_machine模块本身及其测试台,这两个文件都将使用insert_type数据类型,该数据类型描述了我们如何对硬币的面额进行编码,并且将其分离为一个单独的文件是合乎逻辑的。但是,Quartus和ModelSim的编译功能存在一些困难。 Quartus一次编译所有源文件,而ModelSim则分别编译每个文件,以编译Quartus'om产生的覆盖类型insert_type,我基于宏处理器的指令使用了C / C ++ include guard的技术。此外,为的ModelSim可以肯定的是,insert_type中使用soda_machine模块然后在测试平台中,将其描述放入soda_machine_types包中鉴于这些要求,soda_machine_types.sv文件如下:



soda_machine_types.sv
`ifndef soda_machine_types_sv_quard

package soda_machine_types;

	typedef enum logic [1:0] {I1=2'b01, I2=2'b10, I5=2'b11} insert_type;
	
endpackage

`define soda_machine_types_sv_quard
`endif




现在,soda_machine模块本身位于soda_machine.sv文件中



苏打水机
`include "soda_machine_types.sv"
import soda_machine_types::*;


module soda_machine(
	input logic clk,          // Clock 
	input logic reset,        // Active high level
	input insert_type insert,
	output logic pour_water,
	output logic change1,
	output logic change2,
	output logic change22);

	typedef enum logic [2:0] {S0, S1, S2, S3, S4} state_type;
	(* syn_encoding = "default" *) state_type state, nextstate;
	//       
	always_ff @(posedge clk, posedge reset)
	if (reset)
		state <= S0;
	else
		state <= nextstate;
	//            
	always_comb
		case (state)
			S0:
				case (insert)
					I1:
						nextstate = S1;
					I2:
						nextstate = S2;
					I5:
						nextstate = S0;
				endcase
			S1: 
				case (insert)
					I1:
						nextstate = S2;
					I2:
						nextstate = S3;
					I5:
						nextstate = S0;
				endcase
			S2:
				case (insert)
					I1:
						nextstate = S3;
					I2: 
						nextstate = S4;
					I5:
						nextstate = S0;
				endcase
			S3: 
				if (insert == I1)
					nextstate = S4;
				else
					nextstate = S0;
			S4:
				nextstate = S0;
		endcase
	//    
	assign pour_water = (state == S4) | (insert == I5) | (state == S3) & (insert == I2);
	
	assign change1 = (state == S1) & (insert == I5) | (state == S3) & (insert == I5) | (state == S4) & (insert == I2);
							
	assign change2 = (state == S2) & (insert == I5) | (state == S3) & (insert == I5);
	
	assign change22 = (state == S4) & (insert == I5);
	
endmodule




如何对状态机状态进行编码,我留给了Quartus处理。为了指示应该如何精确地进行编码,使用了属性(* syn_encoding =“ default” *)在这里可以看到其他编码选项



应该注意的是,在实际项目中,Miles机器的组合逻辑的输出信号必须存储在寄存器中,并从寄存器的输出馈送到FPGA输出。必须使用同步器将输入信号与时钟频率同步,以避免陷入亚稳态。



要将文件添加到项目,请使用文件->新建“ SystemVerilog HDL文件”并在保存时提供适当的名称。添加这两个文件后,可以对项目进行编译处理->开始编译成功编译后,您可以看到生成的方案Tools-> Netlist Viewers-> RTL Viewer



RTL查看器
image



查看状态机statechart 工具->网表查看器->状态机查看器



状态机查看器
image



在“编码”选项卡上,您可以看到Quartus应用了“ one-hot”编码方案,这是当每个状态使用单独的D触发器,并且S 0状态被编码为0,而不是其他状态被编码为1时,这样做是为了将复位电路简化为初始状态。州。您可能会注意到RTL Viewer并没有完全显示原理图,而是一个概念。要查看原理图,请使用工具->网表查看器->技术地图查看器(调试后)



模拟



基本上,目前我们有一个用于汽水机的电路,但是您需要确保其正常工作,为此,我们编写了一个测试台并将其放置在soda_machine_tb.sv文件中



soda_machine_tb.sv
`include "soda_machine_types.sv"
import soda_machine_types::*;

module soda_machine_tb;

	insert_type insert;
	
	logic [5:0] testvectors[10000:0];
	
	int vectornum, errors;
	
	logic clk, reset, pour_water, change1, change2, change22;
	logic pour_water_expected, change1_expected, change2_expected, change22_expected;
	//  
	soda_machine dut(
		.clk(clk),
		.reset(reset),
		.insert(insert),
		.pour_water(pour_water),
		.change1(change1),
		.change2(change2),
		.change22(change22)
	);
	//   
	always
		#5 clk = ~clk;
	//   
	initial begin
		//     
		$readmemb("../../soda_machine.tv", testvectors);
		vectornum = 0;
		errors = 0;
		clk = 1;
		//   
		reset = 1; #13; reset = 0;
	end
	//   
	always @(posedge clk) begin
		#1; {insert, pour_water_expected, change1_expected, change2_expected, change22_expected} = testvectors[vectornum];
	end
	// ,      
	always @(negedge clk)
		if (~reset) begin
			if ((pour_water !== pour_water_expected) || (change1 !== change1_expected) || (change2 !== change2_expected) ||
				(change22 !== change22_expected)) begin
				$error("%3d test insert=%b\noutputs pour_water=%b (%b expected), change1=%b (%b expected), change2=%b (%b expected), change22=%b (%b expected)", 
					vectornum + 1, insert, pour_water, pour_water_expected, change1, change1_expected, change2, change2_expected, change22, change22_expected);
				errors = errors + 1;
			end
			vectornum = vectornum + 1;
			if (testvectors[vectornum] === 6'bx) begin
				$display("Result: %3d tests completed with %3d errors", vectornum, errors);
				$stop;
			end
		end

endmodule




为了测试我们的模块,使用了测试向量文件soda_machine.tv



苏打水机
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
01_1_0_0_0
10_0_0_0_0
10_0_0_0_0
10_1_1_0_0
11_1_0_0_0
10_0_0_0_0
10_0_0_0_0
11_1_0_0_1
10_0_0_0_0
11_1_0_1_0
01_0_0_0_0
01_0_0_0_0
01_0_0_0_0
11_1_1_1_0




前两位是插入输入,后四位是我们等待输出的内容:pour_water,change1,change2,change22。例如,在文件开始处,连续五次插入卢布硬币,在第五个硬币上,我们等待pour_water信号出现,而更改信号无效。soda_machine.tv文件添加到项目文件->新建“文本文件”中。



为了方便使用ModelSim,请添加具有以下内容soda_machine_run_simulation.do文件



soda_machine_run_simulation.do
add wave /soda_machine_tb/dut/clk
add wave /soda_machine_tb/dut/reset
add wave /soda_machine_tb/dut/insert
add wave /soda_machine_tb/dut/state
add wave /soda_machine_tb/dut/nextstate
add wave /soda_machine_tb/dut/pour_water
add wave /soda_machine_tb/dut/change1
add wave /soda_machine_tb/dut/change2
add wave /soda_machine_tb/dut/change22
view structure
view signals
run -all
wave zoom full




它将运行我们的仿真并在ModelSim中显示波形。将soda_machine_run_simulation.do文件添加到项目文件->新的“ Tcl脚本文件”中。



现在,我们将设置项目,以便自动开始模拟。选择菜单项分配->设置,选择类别EDA工具设置->模拟。在NativeLink设置中,选择“ 编译测试平台:”,然后单击“ 测试平台”按钮...打开“测试平台”窗口中单击“ 新建” ...打开的“ 新测试平台设置”窗口中,填写“ 测试平台名称”字段:soda_machine_tb并点击窗口底部的文件选择按钮...,选择我们的soda_machine_tb.sv文件,然后点击添加按钮。它看起来应该像这样:



图片



在“ 新建测试平台设置”窗口中,单击“ 确定”。 “ 测试基准”窗口应如下所示:

图片



在“ 测试基准”窗口中,单击“ 确定”。在NativeLink设置中,选中“ 使用脚本来设置仿真” 复选框,然后选择soda_machine_run_simulation.do文件。 “ 设置”窗口

应如下所示:



图片



在“ 设置”窗口中,单击好的,我们编译项目Processing-> Start Compilation,运行仿真工具-> Run Simulation Tool-> RTL SimulationModelSim应该启动,项目将进行仿真。文字标签的外观:



ModelSim成绩单选项卡
image



测试台输出的已执行测试次数和检测到的错误均以红色突出显示。波形标签的外观:



ModelSim Wave选项卡
image



项目源代码



该项目的源代码位于github.com/igoral5/soda_machine克隆该项目,然后使用Quartus File-> Open Project ...

选择soda_machine.qpf文件来打开该项目然后编译项目Processing-> Start Compilation并运行仿真工具-> Run Simulation Tool-> RTL Simulation



All Articles