Usando Quartus y ModelSim

Siempre me han interesado los circuitos digitales y, en particular, los lenguajes de descripción de hardware: HDL. Durante mucho tiempo he estado en la lista de futuros libros de lectura de David M. Harris y Sarah L. Harris "Circuitos digitales y arquitectura de computadoras", aprovechando el tiempo libre para el autoaislamiento, llegué a este maravilloso libro. En el proceso de lectura, encontré algunas dificultades, en particular, cómo escribir y depurar código en Quartus Prime. En el proceso de búsqueda, el sitio marsohod.org realmente me ayudó ., pero el proceso de simulación de circuitos en este sitio se describe utilizando las herramientas integradas de Quartus y en las versiones modernas del programa, no existen tales herramientas integradas y debe usar ModelSim. Con el fin de sistematizar de alguna manera el conocimiento que obtuve usando Quartus y ModelSim, decidí escribir este artículo. En el proceso de este artículo, tomaré como ejemplo una tarea del libro de David M. Harris y Sarah L. Harris "Circuitos digitales y arquitectura de una computadora", específicamente la tarea 3.26 sobre una máquina de refrescos. A lo largo de este artículo, le mostraré cómo instalar Quartus, crear un proyecto, escribir código y simularlo. Todos los que estén interesados ​​en esto, bienvenidos debajo del gato.



imagen



Formulación del problema



Te persuadieron para diseñar una máquina de refrescos para la oficina. Las bebidas están parcialmente cubiertas por la unión, por lo que solo cuestan 5 rublos. La máquina acepta monedas de 1, 2 y 5 rublos. Tan pronto como el cliente pague la cantidad requerida, la máquina dispensará la bebida y devolverá el cambio. Diseñe una máquina de estado para una máquina expendedora de refrescos. Las entradas de la máquina son 1, 2 y 5 rublos, a saber, cuál de estas monedas se inserta.



Suponga que para cada señal de reloj, solo se inserta una moneda. La máquina tiene salidas: verter refresco, devolver 1 rublo, devolver 2 rublos, devolver 2 por 2 rublos. Tan pronto como se recogen 5 rublos (o más) en la máquina, establece la señal "Vierta GASING", así como las señales que devuelven el cambio correspondiente. Entonces la máquina debería estar lista para aceptar monedas nuevamente.



Teoría



Las máquinas de estado finito o máquinas de estado finito (FSM) pertenecen a la clase de circuitos secuenciales sincrónicos que representan la gran mayoría de los circuitos digitales. Así es como debe implementar sus proyectos (al menos inicialmente). Este método proporciona repetibilidad y verificación del circuito y es independiente de las relaciones de retardo de los diversos elementos del circuito. Las reglas para construir circuitos secuenciales síncronos establecen que un circuito es un circuito secuencial síncrono si sus elementos satisfacen las siguientes condiciones:



  • Cada elemento del circuito es un registro o un circuito combinacional.
  • Al menos un elemento de esquema es un registro.
  • Todos los registros están sincronizados con una sola señal de reloj.
  • Cada ruta cíclica contiene al menos un registro.


Una máquina de estados tiene varios estados, que almacena en registros. Cuando llega una señal de reloj, la máquina de estado puede cambiar su estado, y cómo cambia exactamente el estado depende de las señales de entrada y el estado actual. En el caso más simple, puede que no haya ninguna señal de entrada, por lo tanto, el divisor de frecuencia funciona. Hay dos clases principales de máquinas de estados finitos: el autómata Moore, en el que las señales de salida dependen solo del estado actual del autómata, y el autómata Mealy, en el que las señales de salida dependen del estado actual y las señales de entrada. En principio, cualquier máquina de estados finitos puede implementarse tanto de acuerdo con el esquema de Moore como de acuerdo con el esquema de Mealy, la diferencia entre ellos será que el autómata de Moore tendrá más estados y estará un reloj detrás de la máquina de Mealy.Para el circuito de la máquina de refrescos, usaré el circuito de Miles. Digamos la máquina de estado:

Símbolo Descripción
S 0 El estado inicial, la cantidad acumulada de 0 rublos.
S 1 La cantidad acumulada es de 1 rub.
S 2 Acumulado 2 rublos.
S 3 Acumulado 3 rublos.
S 4 Acumulado 4 rublos.


Como señal de entrada, actuará un bus de dos bits, con la siguiente codificación del valor nominal de la moneda:

Símbolo Valor Descripción
Yo 1 01 1 frotación
Yo 2 diez 2 frotar
Yo 5 once RUB 5


Dibujemos un diagrama de estado de nuestro autómata (en los diagramas de estado del autómata Mealy, es necesario indicar las señales de salida en las flechas de transición de estado, no lo haré para no saturar el diagrama, todas las señales de salida se describirán en la tabla a continuación):



imagen



escriba la tabla de cambios en los estados y las señales de salida:

Estados Señales de entrada
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 tiene una edición Lite gratuita, que tiene algunas limitaciones en comparación con la edición profesional, la principal limitación es no más de 10,000 líneas de código fuente para la simulación de proyectos. Puede descargarlo, después del registro, por el enlace , en el momento de escribir este artículo, la versión más reciente era 19.1, según el trabajo con esta versión, escribí un artículo. Seleccionamos Lite Edition, versión 19.1, el sistema operativo Windows (debe tenerse en cuenta que hay una versión Quartus para Linux y funciona bien, surgen problemas con ModelSim, que es de 32 bits y usa la versión anterior de la biblioteca de mapeo de fuentes, por lo que al principio recomiendo usar la versión de Windows ), seleccione la pestaña Archivos combinados. El tamaño del archivo para la descarga es muy grande: 5.6 Gb, tenga esto en cuenta. Expande el archivo descargado y ejecutasetup.bat . La instalación se realiza de forma estándar, utilizamos la selección predeterminada de componentes.



Creación de proyectos



Para crear un nuevo proyecto, seleccione el Archivo -> el Nuevo ... por Project Wizard . La primera ventana del asistente es informativa, haga clic en Siguiente , en la segunda ventana, seleccione dónde se ubicará el proyecto, su nombre "soda_machine" y el elemento de diseño de nivel superior "soda_machine" , como en la imagen:



imagen



En la siguiente ventana, seleccione "Proyecto vacío" . La ventana para agregar archivos "Agregar archivos" , no agregue nada. La ventana de selección de dispositivos "Configuración de familia, dispositivos y placa" es muy importante para un proyecto real, pero dado que nuestro proyecto es educativo y está lejos de ser real, dejamos aquí la configuración predeterminada, como en la imagen:



imagen



Ventana para seleccionar configuraciones para otras herramientas"Configuración de la herramienta EDA" , seleccione simular el proyecto para usar "ModelSim-Altera" y el formato "System Verilog HDL" como en la imagen:



imagen



La última ventana de información "Resumen" , haga clic en Finalizar .



Escribir código fuente



Tendremos dos archivos principales con el código fuente, este es el módulo soda_machine en sí y su banco de pruebas, ambos archivos usarán el tipo de datos insert_type , que describe cómo codificamos las denominaciones de monedas y es lógico separarlo en un archivo separado. Pero hay algunas dificultades asociadas con las características de compilación de Quartus y ModelSim. Quartus compila todos los archivos de origen en una sola pasada, y ModelSim compila cada archivo por separado, de modo que al compilar Quartus no habría redefinición del tipo insert_type , utilicé la técnica de C / C ++ incluir protector basado en las directivas del microprocesador. Además, para que ModelSim se asegure de que insert_type se use en el módulo soda_machiney en el banco de pruebas, uno y el mismo, coloque su descripción dentro del paquete soda_machine_types . Con estos requisitos en mente, el archivo soda_machine_types.sv se ve así:



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




Ahora el módulo soda_machine se encuentra en el archivo soda_machine.sv :



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




Cómo se codifican los estados de la máquina de estados, lo dejé hasta Quartus. Para indicar cómo debe realizarse exactamente la codificación, se usa el atributo (* syn_encoding = "default" *) , aquí se pueden ver otras opciones de codificación .



Cabe señalar que en un proyecto real, las señales de salida de la lógica combinacional del autómata Mealy deben almacenarse en registros y, ya desde la salida de los registros, alimentarse a la salida FPGA. Las señales de entrada deben sincronizarse con la frecuencia del reloj mediante sincronizadores, para evitar caer en un estado metaestable.



Para agregar archivos al proyecto, use File -> New "SystemVerilog HDL File"y al guardar dar el nombre apropiado. Después de agregar estos dos archivos, el proyecto se puede compilar con Procesando -> Iniciar compilación . Después de una compilación exitosa, puede ver el esquema resultante Herramientas -> Netlist Viewers -> RTL Viewer :



RTL Viewer
image



Para ver el diagrama de estado de la máquina de estado Herramientas -> Netlist Viewers -> State Machine Viewer



Visor de máquinas de estado
image



En la pestaña Codificación, puede ver que Quartus aplicó el esquema de codificación "one-hot", esto es cuando se usa un disparador D por separado para cada estado, y el estado S 0 se codifica 0, no 1 como para otros estados, esto se hace para simplificar el esquema de reinicio inicial estado. Puede notar que RTL Viewer no muestra exactamente un diagrama esquemático, sino más bien un concepto. Para ver el diagrama esquemático, use Herramientas -> Netlist Viewrs -> Technology Map Viewer (Post-Fitting)



Simulación



En principio, en este momento tenemos un circuito para una máquina para vender agua de soda, pero debe asegurarse de que funcione correctamente, para esto escribimos un banco de pruebas y lo colocamos en el archivo 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




Para probar nuestro módulo, utilizamos el archivo vectorial de prueba soda_machine.tv :



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




Los primeros dos bits son la entrada de inserción, los siguientes 4 bits son nuestras expectativas para las salidas: pour_water, change1, change2, change22. Por ejemplo, al comienzo del archivo, una moneda de rublo se inserta 5 veces seguidas, en la quinta moneda, esperamos que aparezca la señal pour_water, mientras que las señales de cambio están inactivas. El archivo soda_machine.tv se agrega al proyecto Archivo -> Nuevo "Archivo de texto".



Para la conveniencia de trabajar con ModelSim, agregue el archivo soda_machine_run_simulation.do con el siguiente contenido:



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




Ejecutará nuestra simulación y enviará los gráficos de señal a ModelSim. El archivo soda_machine_run_simulation.do se agrega al proyecto Archivo -> Nuevo "Archivo de script Tcl"



Ahora configuraremos el proyecto para que la simulación se inicie automáticamente. Seleccione el elemento de menú Asignaciones -> Configuración , seleccione la categoría Configuración de herramientas EDA -> Simulación . En la configuración de NativeLink, seleccione Compilar banco de pruebas: y haga clic en el botón Bancos de prueba ... en la ventana Bancos de prueba que se abre, haga clic en el botón Nuevo ... En la ventana Nueva configuración de banco de pruebas que se abre, complete el campo Nombre del banco de pruebas: soda_machine_tby haga clic en el botón de selección de archivo ... en la parte inferior de la ventana, seleccione nuestro archivo soda_machine_tb.sv y haga clic en el botón Agregar . Debería verse así:



imagen



en la ventana Nueva configuración de banco de pruebas , haga clic en Aceptar . La ventana Bancos de prueba debería verse así:

imagen



En la ventana Bancos de prueba , haga clic en Aceptar . En la configuración de NativeLink, configure la casilla Usar script para configurar la simulación y seleccione el archivo soda_machine_run_simulation.do . La ventana de configuración

debería verse así:



imagen



en la ventana de configuración , haga clic enOK , compilamos el proyecto Procesando -> Iniciar compilación , ejecutamos las Herramientas de simulación -> Ejecutar herramienta de simulación -> Simulación RTL . ModelSim debería comenzar y el proyecto se simulará. Apariencia de la ficha de transcripción:



Pestaña Transcripción ModelSim
image



El marco rojo marca la salida de nuestro banco de pruebas sobre el número de pruebas realizadas y los errores encontrados. Apariencia de la pestaña Wave:



Pestaña ModelSim Wave
image



Código fuente del proyecto



El código fuente del proyecto está en github.com/igoral5/soda_machine Clone el proyecto, luego abra el proyecto con Quartus File -> Open Project ...

seleccione el archivo soda_machine.qpf . Luego compile el proyecto Procesando -> Iniciar compilación e inicie las Herramientas de simulación -> Ejecutar herramienta de simulación -> Simulación RTL .



All Articles