Usar la herramienta de configuración de Datapath



Tenemos que dar el pen√ļltimo paso en el desarrollo pr√°ctico de trabajar con UDB. Hoy no desarrollaremos usando el editor UDB automatizado, sino en modo semi-manual usando la herramienta de configuraci√≥n de Datapath. Una muy buena ayuda para dominar esta herramienta es AN82156 - PSoC 3, PSoC 4 y PSoC 5LP - Dise√Īo de componentes de PSoC Creator con UDB Datapaths. En realidad, lo estudi√© yo mismo.

Quizás, mientras leía nuestras traducciones de documentación sobre UDB , alguien intentó reproducir el conocimiento desde allí en la práctica y notó que no toda la funcionalidad descrita en las publicaciones está disponible en el Editor de UDB. Esto se debe al hecho de que los desarrolladores no comenzaron a colocar algunos mecanismos particularmente engorrosos en el Editor UDB. Los autores de AN82156 argumentan que a través del Editor UDB no puede hacer lo siguiente:

  • organizar la entrada y salida de datos paralelos;
  • organizar la gesti√≥n din√°mica de FIFO;
  • implementar el inverso de la se√Īal de reloj FIFO;
  • implementar la funci√≥n CRC;
  • implementar la funci√≥n PRS;
  • implementar la elecci√≥n de la transferencia entrante;
  • Implementar la migraci√≥n entrante din√°mica.

Por mi parte, agregaré que no encontré cómo implementar la permutación de nibbles en el Editor UDB.

Si se necesitan estas funciones en el proyecto, deber√° crear su propio c√≥digo Verilog. Us√© espec√≠ficamente la palabra "crear" en lugar de "escribir". Conocer este lenguaje de programaci√≥n es suficiente a nivel de lectura. Quiero decir, debes entender qu√© dise√Īo se necesita para qu√©. Y poder escribir desde cero siempre es √ļtil, pero esta habilidad no es necesaria para lo que se presenta en este art√≠culo.

Como problema solucionable, eleg√≠ un estuche semisint√©tico. En general, decid√≠ enviar algunos datos al puerto paralelo y, en particular, de lo que est√° a la mano, el LCD de texto tiene un puerto paralelo. Lo saqu√© de la impresora 3D MZ3D hace tres a√Īos cuando trasplant√© esta √ļltima a STM32. Por lo tanto, el caso es semisint√©tico: hoy en d√≠a, tales indicadores generalmente tienen una entrada I2C y no necesitan conectarse a trav√©s de una pila de cables en la vida real. Sin embargo, los LCD modernos tambi√©n tienen puertos paralelos, por lo que todos pueden usarlos para repetir el experimento.

Considere el esquema de cambio de pantalla tomado de reprap.org (esto no fue fácil, mi proveedor bloquea este sitio, así como también otros técnicos, motivándolo con el hecho de que vive en la misma IP que alguien bloqueado).



Gran dise√Īo! En primer lugar, no tengo que pensar en leer: los datos en la pantalla LCD solo se pueden escribir (la l√≠nea R / W est√° conectada a tierra y no est√° disponible en el conector). En segundo lugar, los datos vienen en un formato de 4 bits, lo que significa que no solo podemos calcular la salida paralela, sino tambi√©n verificar el funcionamiento de la funci√≥n de permutaci√≥n de mordisco.

Creación de proyectos


Entonces, inicie PSoC Creator y seleccione Archivo-> Nuevo-> Proyecto :



A continuación, elijo mi placa de pruebas:



El siguiente es el diagrama vacío:



Llamaré al proyecto LCDTest2 :



Ahora, como antes, vaya a la pesta√Īa Componentes :



Y, una vez seleccionado el proyecto, presione el botón derecho del mouse y luego seleccione Agregar elemento componente .



Y aquí tienes que elegir el Asistente de símbolos . Dale un nombre ... Bueno, digamos LCD4bit .



Asigné los siguientes puertos al símbolo:



clk es la entrada del reloj. Los puertos con un prefijo LCD son puertos LCD estándar. hambre : salidas que le dicen a la unidad DMA que hay espacio libre en FIFO, la idea se discutió en un artículo sobre el control de LED RGB . Haga clic en Aceptar para obtener el personaje.



Ahora, basado en este s√≠mbolo, se debe generar una plantilla Verilog. Haga clic con el bot√≥n derecho del mouse cerca del s√≠mbolo y seleccione Generar Verilog en el men√ļ contextual.



Obtuvimos la plantilla que se muestra en la figura a continuación (en forma de texto todavía no tiene sentido):



Hemos creado un m√≥dulo y algunas secciones. Pero todav√≠a no han creado Datapath. Para agregarlo, vaya al √°rbol del proyecto, seleccione el archivo LCD4bit.v , presione el bot√≥n derecho del mouse y seleccione la herramienta de configuraci√≥n de Datapath en el men√ļ contextual que aparece:



Se abre una ventana ante nosotros, que por ahora solo mostraré parcialmente:



Por favor, amor y favor, editor de Datapath. Contiene todos los bits que se describieron en la traducci√≥n de la documentaci√≥n patentada. Pero hay tantos de estos bits que en los primeros d√≠as lo mir√©, pero ten√≠a miedo de hacer algo. Mira, mira y sal. Y solo despu√©s de un tiempo, acostumbr√°ndose, comenz√≥ a intentar hacer algo. En realidad, es por eso que traje solo una parte de la ventana. ¬ŅPor qu√© asustar a todos por adelantado? Mientras tanto, solo necesitamos crear un Datapath, por lo que seleccionamos el elemento de men√ļ Editar-> Nuevo Datapath :



¬ŅQu√© opci√≥n elegir en el cuadro de di√°logo que aparece?



La pregunta es un poco más seria de lo que parece. Permítanme incluso resaltar el siguiente párrafo para que nadie quede atrapado (me sorprendí a mí mismo, y luego vi preguntas en la red de las que obtuve, y nadie realmente las respondió, y la respuesta está en AN82156 , solo necesita leerlo en diagonal, como dice allí frase corta y discreta).
Si planea trabajar con datos paralelos, definitivamente debe elegir la opción CY_PSOC3_DP. Ninguna otra opción contendrá puertos para conectar datos paralelos.
Entonces Deje que la instancia se llame LCD_DP:



Haga clic en Aceptar y cierre la herramienta de configuración de Datapath por ahora , aceptando guardar el resultado. Volveremos aquí más tarde.

Nuestro código Verilog se ha expandido. Ahora tiene Datapath. Su comienzo es completamente ilegible. No da miedo, está configurado por la herramienta de configuración de Datapath .



Y gobernaremos el final de la descripción de Datapath. Nuestro sitio se ve así
(desde este punto tiene sentido traer todo en forma de texto).
)) LCD_DP( /* input */ .reset(1'b0), /* input */ .clk(1'b0), /* input [02:00] */ .cs_addr(3'b0), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(1'b0), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(), /* output */ .f0_blk_stat(), /* output */ .f1_bus_stat(), /* output */ .f1_blk_stat(), /* input */ .ci(1'b0), // Carry in from previous stage /* output */ .co(), // Carry out to next stage /* input */ .sir(1'b0), // Shift in from right side /* output */ .sor(), // Shift out to right side /* input */ .sil(1'b0), // Shift in from left side /* output */ .sol(), // Shift out to left side /* input */ .msbi(1'b0), // MSB chain in /* output */ .msbo(), // MSB chain out /* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage /* output [01:00] */ .ceo(), // Compare equal out to next stage /* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage /* output [01:00] */ .clo(), // Compare less than out to next stage /* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage /* output [01:00] */ .zo(), // Zero detect out to next stage /* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage /* output [01:00] */ .fo(), // 0xFF detect out to next stage /* input [01:00] */ .capi(2'b0), // Software capture from previous stage /* output [01:00] */ .capo(), // Software capture to next stage /* input */ .cfbi(1'b0), // CRC Feedback in from previous stage /* output */ .cfbo(), // CRC Feedback out to next stage /* input [07:00] */ .pi(8'b0), // Parallel data port /* output [07:00] */ .po() // Parallel data port ); 


Miedo Ahora descubriremos qu√© es qu√©, dejar√° de dar miedo. De hecho, hay tres grupos distintos en este texto. Recordemos la traducci√≥n de la documentaci√≥n. ¬ŅC√≥mo se ve√≠a la ruta de datos en la imagen? Notar√© de inmediato en la figura los lugares a los que pertenecen los grupos "1", "2" y "3".



En realidad, el primer grupo de puertos en el c√≥digo verilog son las entradas. Compare los nombres en la salida del multiplexor de entrada ("1" en la figura) y los nombres de las se√Īales en el c√≥digo.

Ahora todas las entradas son cero. Tendremos que conectar la entrada del reloj y podemos reenviar hasta seis líneas de entrada, como se hizo en el Editor UDB. Estas entradas son:

  /* input */ .reset(1'b0), /* input */ .clk(1'b0), /* input [02:00] */ .cs_addr(3'b0), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(1'b0), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), 

El segundo grupo son las salidas. Los nombres en el código también coinciden con los nombres de las entradas del multiplexor de salida "2":

  /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(), /* output */ .f0_blk_stat(), /* output */ .f1_bus_stat(), /* output */ .f1_blk_stat(), 

Solo la especie Datapath dada tiene el tercer grupo (los otros no tienen ninguno, por lo tanto, no hay datos paralelos). Estas son se√Īales internas de Datapath a trav√©s de las cuales puede encadenar independientemente o realizar otras acciones √ļtiles. Los nombres en el c√≥digo tambi√©n coinciden con los nombres de las se√Īales internas dispersas en la figura. A trav√©s de uno de ellos (el √ļltimo en la lista, su nombre es po ) enviaremos datos paralelos directamente a las patas del chip.

  /* input */ .ci(1'b0), // Carry in from previous stage /* output */ .co(), // Carry out to next stage /* input */ .sir(1'b0), // Shift in from right side /* output */ .sor(), // Shift out to right side /* input */ .sil(1'b0), // Shift in from left side /* output */ .sol(), // Shift out to left side /* input */ .msbi(1'b0), // MSB chain in /* output */ .msbo(), // MSB chain out /* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage /* output [01:00] */ .ceo(), // Compare equal out to next stage /* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage /* output [01:00] */ .clo(), // Compare less than out to next stage /* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage /* output [01:00] */ .zo(), // Zero detect out to next stage /* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage /* output [01:00] */ .fo(), // 0xFF detect out to next stage /* input [01:00] */ .capi(2'b0), // Software capture from previous stage /* output [01:00] */ .capo(), // Software capture to next stage /* input */ .cfbi(1'b0), // CRC Feedback in from previous stage /* output */ .cfbo(), // CRC Feedback out to next stage /* input [07:00] */ .pi(8'b0), // Parallel data port /* output [07:00] */ .po() // Parallel data port ); 

Entonces A medida que trabajemos, tendremos que conectar algunas de estas entradas y salidas a nuestras propias entidades, y el resto, simplemente dejarlas en la forma en que las creamos.

Usando el editor UDB como referencia


Y ahora tenemos un espacio en blanco, sabemos dónde y qué tenemos que escribir. Queda por entender exactamente en qué entraremos allí. Dio la casualidad de que uso el idioma Verilog no todos los días, por lo que, en términos generales, recuerdo todo, y escribir desde cero para mí siempre es una situación estresante. Cuando el proyecto ya está en marcha, todo se recuerda, pero si después de un par de meses de inactividad empiezo algo desde cero, por supuesto, ya no recuerdo los detalles de sintaxis de este lenguaje en particular. Por lo tanto, sugiero pedir al entorno de desarrollo que nos ayude.

UDB Editor para autocontrol construye el código Verilog. Aprovechamos el hecho de que los componentes que no están involucrados en el circuito principal no están compilados, por lo que podemos crear un componente auxiliar en el editor UDB, y no entrará en el código de salida. Dibujaremos un autómata allí, haremos un ajuste aproximado de las entradas y salidas de Datapath, y luego simplemente transferiremos el texto generado automáticamente a nuestro módulo verilog y modificaremos todo creativamente. Esto es mucho más simple que recordar los detalles de la sintaxis de Verilog y escribir todo desde cero (aunque quien use Verilog constantemente, por supuesto, será más fácil escribir desde cero: la finalización creativa, como veremos pronto, es simple, pero requiere tiempo)

Entonces, comenzamos a hacer un componente auxiliar. Con el movimiento habitual de la mano agregamos un nuevo elemento al proyecto:



Este será un documento UDB, llamémoslo UDBhelper :



Es hora de pensar en la máquina, que colocaremos en la hoja creada. Para hacer esto, debemos considerar qué diagrama de tiempo debemos formar con él:





Entonces Primero debe configurar la se√Īal RS (ya que R / W est√° soldado a cero en el hardware). A continuaci√≥n, espere tAS, luego eleve la se√Īal E y configure los datos (la configuraci√≥n de datos con respecto al borde positivo E no est√° limitada). Los datos deben estar en el bus no menos que tDSW, despu√©s de lo cual se debe eliminar la se√Īal E. Los datos deben permanecer en el bus durante al menos tDHW, y RS durante al menos tAH.

RS es el comando o el indicador de datos. Si RS es cero, se escribe un comando, si es uno, se escriben los datos.

Sugiero enviar comandos a través de FIFO0 y datos a través de FIFO1 . En el marco de la tarea actual, esto no contradice nada. Entonces la máquina de estados finitos propuesta por mí tendrá la siguiente forma:



En el estado inactivo , la m√°quina a√ļn no tiene datos FIFO. Si aparecieron datos en FIFO0 , va a LoadF0 , donde en el futuro recibir√° datos de FIFO0 a A0.

Mientras se transmiten los comandos, los datos no deben enviarse. Por lo tanto, la condición para recibir datos tendrá menor prioridad que la condición para recibir comandos.



Los datos se reciben en A1 en el estado LoadF1 (desde FIFO1 solo pueden ir al registro A1 y no pueden ir al registro A0), y luego se copian de A1 a A0 en el estado A1toA0 .

De cualquier manera que vayamos al punto de convergencia de las flechas, tenemos datos en A0. Ya se envían al puerto paralelo. Martillamos E (en estado E_UP1 ), soltamos E (en estado E_DOWN1 ). A continuación, tendremos un estado para intercambiar nibbles ( SWAP ), después de lo cual E se eleva nuevamente ( E_UP2 ). Sobre esto, he agotado ocho estados que pueden codificarse en tres bits. Y recordamos que la RAM de configuración dinámica de Datapath tiene solo tres entradas de dirección. Se podrían aplicar algunos trucos, pero el artículo ya es grande. Por lo tanto, solo la segunda vez colocaremos E en el estado Inactivo . Entonces ocho estados son suficientes para nosotros.

También colocamos Datapath en la hoja y asignamos sus entradas y salidas de una manera que es familiar en los artículos anteriores. Aquí están las entradas:



Aquí están las salidas:



Nada nuevo, todo ya se ha descrito en artículos anteriores del ciclo. Entonces, tenemos un espacio en blanco, en base al cual podemos hacer algo por nuestra cuenta. Es cierto, para asegurarnos de que todo esté funcionando, necesitamos llevar nuestro sistema al nivel superior del proyecto, de lo contrario no se encontrarán errores. Y en los experimentos iniciales sin errores no funcionará. Por lo tanto, haremos una acción auxiliar más.

La descripci√≥n de c√≥mo se realiza el circuito va m√°s all√° de la descripci√≥n de trabajar con UDB. Solo te mostrar√© qu√© circuito tengo. Solo hay una unidad DMA: cuando se env√≠an comandos a la pantalla LCD, es necesario soportar grandes pausas, por lo que a√ļn es m√°s f√°cil hacerlo mediante programaci√≥n. Para otras aplicaciones, simplemente puede poner el segundo bloque DMA por analog√≠a usando la se√Īal hungry0 .



Para cumplir con precisión el marco de tiempo, elegí una frecuencia de reloj igual a un megahercio. Sería posible tomar una frecuencia y más, pero los datos se transmiten a través de cables largos en condiciones de alta interferencia, por lo que es mejor tomarse el tiempo para configurar los datos antes y después de la puerta con un margen. Si alguien repite mis experimentos en la misma placa de prueba, no use el puerto P3.2: un condensador está soldado a esta pata en la placa. Maté durante media hora, hasta que descubrí por qué no formé un impulso E, que primero conecté allí. Lo lancé a P3.1, todo funcionó de inmediato. Mi bus de datos va a P3.7-P3.4, RS va a P3.3, por lo que E originalmente fue a P3.2 ...

Bueno aqui. Ahora, si intenta compilar el proyecto, obtenemos errores completamente predecibles



Entonces el sistema est√° tratando de recolectar algo. Pero ella todav√≠a no tiene nada que coleccionar. Procedemos a copiar el c√≥digo. Para hacer esto, en el Editor UDB, cambie a la pesta√Īa Verilog (esta pesta√Īa se encuentra debajo de la ventana con la hoja del Editor UDB):



¬ŅQu√© es familiar all√≠? Al final del texto est√° el cuerpo del aut√≥mata. Comencemos la migraci√≥n desde all√≠.

También colóquelo debajo de Datapath:
 /* ==================== State Machine: SM ==================== */ always @ (posedge clock) begin : Idle_state_logic case(SM) Idle : begin if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; end else if (( !F1empty ) == 1'b1) begin SM <= LoadF1 ; end end LoadF0 : begin if (( 1'b1 ) == 1'b1) begin SM <= E_Up1 ; end end E_Up1 : begin if (( 1'b1 ) == 1'b1) begin SM <= E_Down1 ; end end E_Down1 : begin if (( 1'b1 ) == 1'b1) begin SM <= SWAP ; end end SWAP : begin if (( 1'b1 ) == 1'b1) begin SM <= E_UP2 ; end end E_UP2 : begin if (( 1'b1 ) == 1'b1) begin SM <= Idle ; end end LoadF1 : begin if (( 1'b1 ) == 1'b1) begin SM <= A1toA0 ; end end A1toA0 : begin if (( 1'b1 ) == 1'b1) begin SM <= E_Up1 ; end end default : begin SM <= Idle; end endcase end 


Hay declaraciones en la parte superior de este código (nombres para estados, cadenas para Datapath, un registro que codifica el estado de un autómata). Los transferimos al
sección de nuestro código:
 /* ==================== Wire and Register Declarations ==================== */ localparam [2:0] Idle = 3'b000; localparam [2:0] LoadF0 = 3'b001; localparam [2:0] LoadF1 = 3'b010; localparam [2:0] E_Up1 = 3'b100; localparam [2:0] A1toA0 = 3'b011; localparam [2:0] E_Down1 = 3'b101; localparam [2:0] SWAP = 3'b110; localparam [2:0] E_UP2 = 3'b111; wire hungry0; wire F0empty; wire hungry1; wire F1empty; wire Datapath_1_d0_load; wire Datapath_1_d1_load; wire Datapath_1_f0_load; wire Datapath_1_f1_load; wire Datapath_1_route_si; wire Datapath_1_route_ci; wire [2:0] Datapath_1_select; reg [2:0] SM; 


Bueno y

el sitio de uni√≥n de se√Īal es transferible:
 /* ==================== Assignment of Combinatorial Variables ==================== */ assign Datapath_1_d0_load = (1'b0); assign Datapath_1_d1_load = (1'b0); assign Datapath_1_f0_load = (1'b0); assign Datapath_1_f1_load = (1'b0); assign Datapath_1_route_si = (1'b0); assign Datapath_1_route_ci = (1'b0); assign Datapath_1_select[0] = (SM[0]); assign Datapath_1_select[1] = (SM[1]); assign Datapath_1_select[2] = (SM[2]); 


Es hora de conectar Datapath. El código portado desde el editor UDB es bueno para la edición automática, pero no muy bueno para la edición manual. Allí, se crean cadenas que se conectan a las entradas de Datapath en un extremo y a las constantes en el otro. Pero en el código creado por la Herramienta de configuración de Datapath (que hace todo para el trabajo manual), todas las entradas ya están conectadas directamente a constantes cero. Así que conectaré solo aquellas líneas que no son constantes, pero cortaré todo lo relacionado con el reenvío de constantes del texto transferido. La conexión resultó así (el color resalta los lugares que edité con respecto a los creados automáticamente en la Herramienta de configuración de Datapath):



Mismo texto:
 )) LCD_DP( /* input */ .reset(1'b0), /* input */ .clk(clk), /* input [02:00] */ .cs_addr(SM), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(1'b0), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(hungry0), /* output */ .f0_blk_stat(F0empty), /* output */ .f1_bus_stat(hungry1), /* output */ .f1_blk_stat(F1empty), 


Los datos paralelos son un poco m√°s complicados. Datapath tiene un puerto de ocho bits, y solo cuatro de ellos deben sacarse. Por lo tanto, comenzamos el circuito auxiliar y conectamos solo la mitad a la salida:

 wire [7:0] tempBus; assign LCD_D = tempBus[7:4]; 

Y conéctelo así:



Mismo texto:
  /* input [07:00] */ .pi(8'b0), // Parallel data port /* output [07:00] */ .po( tempBus) // Parallel data port ); 


Intentamos ensamblar (Shift + F6 o mediante el elemento de men√ļ Build-> Generate Application ). Obtenemos el error:



Tenemos puertos hambriento0 y hambriento1 (aparecieron al crear el componente), as√≠ como cadenas del mismo nombre (aparecieron al arrastrar desde la muestra). Simplemente quite estas cadenas (dejando los puertos). Y en alg√ļn lugar la se√Īal del reloj se filtr√≥, y tenemos este circuito llamado clk .

Después de eliminar todos los circuitos innecesarios (aquellos que inicialmente arrojaron constantes cero a las entradas de Datapath, así como hungry0 y hungry1 ), obtenemos el siguiente código para el comienzo de nuestro archivo:

 // Your code goes here /* ==================== Wire and Register Declarations ==================== */ localparam [2:0] Idle = 3'b000; localparam [2:0] LoadF0 = 3'b001; localparam [2:0] LoadF1 = 3'b010; localparam [2:0] E_Up1 = 3'b100; localparam [2:0] A1toA0 = 3'b011; localparam [2:0] E_Down1 = 3'b101; localparam [2:0] SWAP = 3'b110; localparam [2:0] E_UP2 = 3'b111; wire F0empty; wire F1empty; reg [2:0] SM; /* ==================== Assignment of Combinatorial Variables ==================== */ wire [7:0] tempBus; assign LCD_D = tempBus[7:4]; 

Y al reemplazar el reloj con clk en el cuerpo de la máquina, al mismo tiempo arrojaré todas las líneas que son buenas para la generación automática, pero con la edición manual solo crean confusión (todas las comparaciones que dan un resultado incondicional VERDADERO y así sucesivamente). En particular, en el ejemplo a continuación, puede tachar aproximadamente la mitad de las líneas (y algunas de inicio / final son opcionales, a veces serán necesarias, porque agregaremos acciones, las destaqué):



Después de peinar de acuerdo con el principio anterior (y reemplazar el reloj con clk ), dicho cuerpo permanece

(se ha vuelto m√°s corto, lo que significa que es m√°s f√°cil de leer):
 always @ (posedge clk) begin : Idle_state_logic case(SM) Idle : begin if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; end else if (( !F1empty ) == 1'b1) begin SM <= LoadF1 ; end end LoadF0 : begin SM <= E_Up1 ; end E_Up1 : begin SM <= E_Down1 ; end E_Down1 : begin SM <= SWAP ; end SWAP : begin SM <= E_UP2 ; end E_UP2 : begin SM <= Idle ; end LoadF1 : begin SM <= A1toA0 ; end A1toA0 : begin SM <= E_Up1 ; end default : begin SM <= Idle; end endcase end 


Ahora, durante la compilación, se nos dice que los circuitos LCD_E y LCD_RS no están conectados.

En realidad, esto es cierto:



Ha llegado el momento de agregar acción a la máquina de estado. Reemplazaremos las declaraciones de los puertos correspondientes a las cadenas no conectadas con reg , ya que las escribiremos en el cuerpo de la máquina (esta es la sintaxis del lenguaje Verilog, si escribimos, los datos deben hacer clic, para esto necesitamos un disparador, y está dada por la palabra clave reg ):


Mismo texto:
 module LCD4bit ( output hungry0, output hungry1, output [3:0] LCD_D, output reg LCD_E, output reg LCD_RS, input clk ); 


Y llene la máquina con acciones. Ya dije la lógica anterior cuando estaba considerando el gráfico de transición del autómata, por lo que solo mostraré el resultado:


Mismo texto:
 always @ (posedge clk) begin : Idle_state_logic case(SM) Idle : begin LCD_E <= 0; if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; LCD_RS <= 0; end else if (( !F1empty ) == 1'b1) begin SM <= LoadF1 ; LCD_RS <= 1; end end LoadF0 : begin SM <= E_Up1 ; end E_Up1 : begin SM <= E_Down1 ; LCD_E <= 1'b1; end E_Down1 : begin SM <= SWAP ; LCD_E <= 1'b0; end SWAP : begin SM <= E_UP2 ; end E_UP2 : begin SM <= Idle ; LCD_E <= 1; end LoadF1 : begin SM <= A1toA0 ; end A1toA0 : begin SM <= E_Up1 ; end default : begin SM <= Idle; end endcase end 


A partir de este momento, el proyecto comienza a ensamblarse. Pero a√ļn no va a trabajar. Hasta ahora, he dicho: "En este estado, cargaremos el registro desde FIFO", "En esto, A1 se copiar√° a A0", "Los nibbles se reorganizar√°n en esto". En general, habl√© mucho, pero hasta ahora no ha habido acciones. Ha llegado el momento de cumplirlos. Vemos c√≥mo se codificaron los estados:

 localparam [2:0] Idle = 3'b000; localparam [2:0] LoadF0 = 3'b001; localparam [2:0] LoadF1 = 3'b010; localparam [2:0] E_Up1 = 3'b100; localparam [2:0] A1toA0 = 3'b011; localparam [2:0] E_Down1 = 3'b101; localparam [2:0] SWAP = 3'b110; localparam [2:0] E_UP2 = 3'b111; 

Vuelva a abrir la herramienta de configuración de Datapath :



Y comience a editar las líneas CFGRAM . Al editar, debe tener en cuenta el esquema Datapath, a saber:



Los cuadros rojos en la figura siguiente (y las flechas en la figura anterior) resaltaron las áreas corregidas (y la ruta de datos) para el estado LoadF0 (código 001, es decir, Reg1 ). También ingresé comentarios manualmente. El contenido de F0 debe entrar en A0.



Con marcos y flechas verdes marqué la configuración y la ruta para el estado LoadF1 (código 010 - Reg2 ).

Con marcos azules y flechas marqué la configuración y la ruta para el estado A1toA0 (código 011 - Reg3 ).

Los cuadros y flechas morados marqué la configuración y la ruta para el estado de SWAP (código 110 - Reg6 ).

Finalmente, las flechas naranjas muestran la ruta de datos paralela. Y no se toman medidas por ellos. Siempre salen de la SRCA . Casi siempre tenemos A0 seleccionado como SRCA : los datos provienen de A0. Entonces, para redirigir los datos de entrada, tendr√≠amos que realizar muchas acciones auxiliares, pero no aceptamos ning√ļn dato, por lo que aqu√≠ no necesitamos estas acciones, y todos encontrar√°n su lista en AN82156 . Tampoco necesitamos editar ninguna configuraci√≥n est√°tica de Datapath, as√≠ que cierre la herramienta de configuraci√≥n de Datapath .

Eso es todo. Hardware concebido completado. Comenzar a desarrollar c√≥digo C. Para hacer esto, vaya a la pesta√Īa Fuente y edite el archivo main.c.



La inicialización regular de LCD y la salida de caracteres "ABC" se ven así (le recuerdo que los comandos van a FIFO0 , la documentación necesita insertar pausas entre los equipos, y los datos van a FIFO1 , no encontré nada sobre las pausas entre los datos):

  volatile uint8_t* pFIFO0 = (uint8_t*) LCD4bit_1_LCD_DP__F0_REG; volatile uint8_t* pFIFO1 = (uint8_t*) LCD4bit_1_LCD_DP__F1_REG; pFIFO0[0] = 0x33; CyDelay (5); pFIFO0[0] = 0x33; CyDelay (100); pFIFO0[0] = 0x33; CyDelay (5); pFIFO0[0] = 0x20; CyDelay (5); pFIFO0[0] = 0x0C; //   CyDelay (50); pFIFO0[0] = 0x01; //   CyDelay (50); pFIFO1[0] = 'A'; pFIFO1[0] = 'B'; pFIFO1[0] = 'C'; 

Que es ¬ŅPor qu√© solo hay el primer personaje en la pantalla?



Y si agrega demoras entre la salida de datos, todo est√° bien:



El osciloscopio no tiene suficientes canales para tal trabajo. Verificamos el trabajo en un analizador lógico. El proceso de grabación de datos es el siguiente.



Todos los datos están en su lugar (tres pares de paquetes). El tiempo de instalación y ajuste de datos se asigna en un volumen suficiente. En general, desde el punto de vista de los diagramas de tiempo, todo se hace correctamente. El problema científico se resuelve, se forman los diagramas de tiempo deseados. Aquí está la ingeniería, no. La razón de esto es la lentitud del procesador instalado en la pantalla LCD. Entre bytes, agregue retrasos.

Formaremos demoras usando un contador de siete bits, al mismo tiempo entrenaremos para agregarlo a dicho sistema. Mantengámonos en estado inactivo no menos de un tiempo determinado, y un contador de siete bits medirá este tiempo para nosotros. Y nuevamente, no escribiremos, sino que crearemos código. Por lo tanto, nuevamente vamos al componente auxiliar del Editor UDB y agregamos un contador a la hoja de trabajo, configurando sus parámetros de la siguiente manera:



Este contador siempre funcionar√° ( Habilitar se establece en 1). Pero se cargar√° cuando la m√°quina est√© en el estado E_UP2 (despu√©s de lo cual caeremos inmediatamente en el estado inactivo ). La l√≠nea Count7_1_tc se elevar√° a 1 cuando el contador cuente a cero, lo que crearemos una condici√≥n adicional para salir del estado inactivo . La figura tambi√©n contiene el valor del per√≠odo, pero no lo encontraremos en el c√≥digo Verilog. Tendr√° que ser ingresado en el c√≥digo C. Pero primero, transferimos el c√≥digo Verilog generado autom√°ticamente al cambiar a la pesta√Īa Verilog. En primer lugar, el contador debe estar conectado (vemos este c√≥digo al principio del archivo y tambi√©n lo movemos al principio):

 `define CY_BLK_DIR "$CYPRESS_DIR\..\psoc\content\CyComponentLibrary\CyComponentLibrary.cylib\Count7_v1_0" `include "$CYPRESS_DIR\..\psoc\content\CyComponentLibrary\CyComponentLibrary.cylib\Count7_v1_0\Count7_v1_0.v" 

Ya se ha descrito cómo se realiza el refinamiento creativo de líneas y constantes, así que solo mostraré el resultado. Aquí están las cadenas y las tareas agregadas como resultado (el resto establece las constantes, así que las tiré):

 wire Count7_1_load; wire Count7_1_tc; assign Count7_1_load = (SM==E_UP2); 

Y aquí está el contador en sí, colocado al final del archivo. Todas las constantes se asignan a los puertos directamente en esta declaración:

  Count7_v1_0 Count7_1 ( .en(1'b1), .load(Count7_1_load), .clock(clk), .reset(1'b0), .cnt(), .tc(Count7_1_tc)); defparam Count7_1.EnableSignal = 1; defparam Count7_1.LoadSignal = 1; 

Para permitir que este contador funcione, agregamos automáticamente una condición adicional para salir del estado Inactivo :


Mismo texto:
  case(SM) Idle : begin LCD_E <= 0; if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; LCD_RS <= 0; end else if (( !F1empty &Count7_1_tc ) == 1'b1) begin SM <= LoadF1 ; LCD_RS <= 1; end end 


La API para el contador agregado de esta manera no se crea, por lo que agregamos dos líneas mágicas a la función principal , que formé en la imagen y semejanza de lo que vi en la API de proyectos anteriores (la primera línea establece el valor cargado de la cuenta, la misma Carga, la segunda inicia el contador):

  *((uint8_t*)LCD4bit_1_Count7_1_Counter7__PERIOD_REG) = 0x20; *((uint8_t*)LCD4bit_1_Count7_1_Counter7__CONTROL_AUX_CTL_REG) |= 0x20; // Start 

El analizador muestra que en el caso modificado el retraso es obvio:



La pantalla LCD también tiene los tres caracteres.

Pero la producci√≥n de caracteres program√°ticos en la vida real es inaceptable. Solo agregarlos a FIFO se desbordar√°. Espere a que el FIFO se vac√≠e, esto significa crear grandes demoras para el n√ļcleo del procesador.El procesador funciona a una frecuencia de 72 MHz, y los datos se emiten durante 7-8 ciclos de reloj a una frecuencia de 1 MHz. Por lo tanto, en la vida real, el texto debe mostrarse usando DMA. Aqu√≠ es donde el principio de "lanzamiento y olvido" es √ļtil. UDB generar√° todos los retrasos para el diagrama de temporizaci√≥n, y el controlador DMA determinar√° la disponibilidad de FIFO para recibirnos datos. El n√ļcleo del procesador solo necesita formar una l√≠nea en la memoria y configurar DMA, despu√©s de lo cual puede realizar otras tareas sin preocuparse por la salida a la pantalla LCD.

Agregue el siguiente código:
  static const char line[] = "This is a line"; /* Defines for DMA_D */ #define DMA_D_BYTES_PER_BURST 1 #define DMA_D_REQUEST_PER_BURST 1 /* Variable declarations for DMA_D */ /* Move these variable declarations to the top of the function */ uint8 DMA_D_Chan; uint8 DMA_D_TD[1]; /* DMA Configuration for DMA_D */ DMA_D_Chan = DMA_D_DmaInitialize(DMA_D_BYTES_PER_BURST, DMA_D_REQUEST_PER_BURST, HI16(line), HI16(LCD4bit_1_LCD_DP__F1_REG)); DMA_D_TD[0] = CyDmaTdAllocate(); CyDmaTdSetConfiguration(DMA_D_TD[0], sizeof(line)-1, CY_DMA_DISABLE_TD, CY_DMA_TD_INC_SRC_ADR); CyDmaTdSetAddress(DMA_D_TD[0], LO16((uint32)line), LO16((uint32)LCD4bit_1_LCD_DP__F1_REG)); CyDmaChSetInitialTd(DMA_D_Chan, DMA_D_TD[0]); CyDmaChEnable(DMA_D_Chan, 1); 


En la pantalla tenemos:



Conclusión


, , UDB ‚ÄĒ Datapath Config Tool. , UDB Editor, UDB, , UDB Editor. , , , UDB Editor.

.

Source: https://habr.com/ru/post/442572/


All Articles