Herramientas Personales

Clase del viernes 14 de febrero de 2014

por Martín Guillén, Sergio Última modificación 18/02/2014 16:09

(Los deberes para la siguiente clase son los "deberes por defecto", es decir, lo lógico cuando no se manda una tarea más específica: "estudiarse el PDF con las transparencias del tema")

 

 

En esta segunda clase se trataron las siguientes cuestiones:

  • A pesar de el énfasis que se hizo en la clase de presentación sobre la necesidad de ser puntuales para poder empezar las clases a la hora prevista, numerosos alumnos llegaron tarde, interrumpiendo el normal desarrollo de la misma. Se advirtió de nuevo que no se deben volver a repetir este tipo de situaciones.
  • En el resumen publicado en la web de la primera clase de Verilog se propusieron 4 ejercicios que podían ser resueltos con lo explicado en dicha clase. En esta clase nos hemos dedicado a resolver en la pizarra esos cuatro ejercicios.
  • En concreto, lo que hicimos fue describir en Verilog cuatro circuitos combinacionales sencillos usando la técnica de descripción "funcional" (o de "flujo de datos") y también mediante la técnica de descripción "procedimental":
  1. Un multiplexor 2:1 con una sola entrada de selección S, una salida Z y dos canales de entrada A y B (siendo A el de menor peso).
  2. Un multiplexor 4:1 con dos entradas de selección S y T (siendo S la de menor peso), una salida Z y cuatro canales de entrada A, B, C y D (siendo A el de menor peso).
  3. Una puerta XOR con dos entradas A y B y una salida Z.
  4. Un decodificador 2:4 con dos entradas S y T (siendo S la de menor peso) y cuatro salidas A, B, C y D (siendo A la de menor peso).
  • Durante la resolución de esos cuatro ejercicios aparecieron, obviamente, muchos conceptos de Verilog vistos en la clase anterior pero también algunos nuevos:
  1. Las "input" y las "output" de un modulo son de tipo "wire" si no se especifica lo contrario.
  2. Las "input"  de un módulo no pueden ser de tipo "reg".
  3. Las "output" de un módulo pueden ser "wire" o "reg". Esto afectará a la forma en que podemos asignar un valor a dicha señal de salida.
    • Si la señal de salida es de tipo "reg" no podremos usar una sentencia de asignación continua (sentencia "assign") para asignarle valor.  Tendremos que asignarle valor utilizando una sentencia de asignación procedimental dentro de un bloque procedimental (por ejemplo dentro de un bloque "always").
    • Si la señal de salida es de tipo "wire" no podremos asignarle valor utilizando una sentencia de asignación procedimental dentro de un bloque procedimental (por ejemplo dentro de un bloque "always"). Tendremos que asignarle valor utilizando una sentencia de asignación continua (sentencia "assign") o bien (y esto no lo hemos visto en clase aún) podemos conectar el "wire" a la salida de la instancia de otro módulo que forme parte del módulo que estamos describiendo (es la técnica de descripción "estructural").
  4. La sentencia "assign" solo podemos usarla sobre una señal de tipo "wire". La expresión que aparece en la parte derecha del "=" puede trabajar sobre datos de cualquier tipo ("wire" o "reg") y usar cualquiera de los operadores de Verilog. No hemos visto todos los operadores que hay (de momento solo &, | y ~) pero el alumno debe ir consultando el tema para irse familiarizando con los demás.
  5. Dentro de un bloque procedimental "always" sabemos que podemos usar sentencias procedimentales como la sentencia de asignación procedimental y la sentencia "if". Las señales "consultadas" o "leídas" dentro de este bloque pueden ser de tipo "reg" o "wire".
  6. Las sentencias de asignación procedimental solo pueden asignarle valor a una señal de tipo "reg". Si nos encontramos con el "problema" de tener que asignarle valor a un "wire" y queremos hacerlo con una sentencia de asignación procedimental, podemos solventarlo declarando una señal auxiliar de tipo "reg" a la cual le demos valor con la sentencia de asignación procedimental y luego usaríamos un "assign" para asignarle al "wire" el valor del "reg" auxiliar.
  7. Justo detrás de la palabra reservada "always" aparece la "lista de sensibilidad".
    • La presencia de la lista de sensibilidad hace que se "entre" dentro del bloque "always" solo cuando cambia el valor de alguna de las señales presentes en dicha lista.
    • Por eso, cuando estamos diseñando un bloque "always" para generar una salida que depende de forma combinacional de una serie de entradas, todas esas entradas deben aparecer en la "lista de sensibilidad" del bloque "always". Esto es así porque las salidas combinacionales deben "recalcularse" siempre que cambie una de  entradas de las que dependen y para "recalcularlas" hay que "entrar" en el.
  8. Con lo bloques "always" que describen circuitos combinacionales tenemos tener la precaución de contemplar todas las posibilidades que pueden darse cuando se entra en el interior de dicho bloque. Para toda combinación de valores posible de las variables de la lista de sensibilidad debe calcularse un valor y asignarse a las variables de salida. De lo contrario la salida que se está describiendo no será "combinacional" sino que será "secuencial", es decir, dependerá de las entradas pero también de su "estado" o "historia pasada" (y eso no es lo que queremos que ocurra pues estamos describiendo un circuito combinacional).
  9. Dentro de un bloque "always" solo puede existir una sentencia procedimental. Si se necesita incluir más de una sentencia, deben englobarse todas ellas en un bloque "begin-end" y será dicho bloque "begin-end" el que aparezca dentro del bloque "always" como única sentencia.
  10. Las sentencias "if" permiten ejecutar una sentencia dependiendo de una condición. Si en lugar de una sentencia se trata de un conjunto de ellas que dependen de la misma condición, las englobaremos dentro de un bloque "begin-end" que será el que dependerá de la condición expresada en el "if".
  11. Vimos como los valores numéricos pueden expresarse en decimal (0, 1 , 2, 3) pero también en binario (2'b00, 2'b01, 2'b10, 2'b11). Hay más detalles de esta notación en las transparencias del tema.
  12. Cuando dentro de una sentencia "always" hay que considerar un número elevado de posibilidades, podemos hacer lo siguiente:
    • Emplear una cadena de sentencias "if", "else if", "else if", etc.
    • Emplear una sentencia "case". La sentencia "case" permite evaluar una única vez una expresión y en función del valor que tenga ejecutar la sentencia procedimental adecuada.
    • Al ilustrar el uso de la sentencia "case" surgió un operador muy importante, el operador de concatenación "{" y "}".
  13. El operador de concatenación "{" y "}" permite hacer muchas cosas que sin él serían muy engorrosas o incluso imposibles de hacer.
    • begin a=0; b=1; c=1; d=1; end es equivalente a hacer { a, b, c, d } = 4'b0111; y equivalente a hacer { a, b, c, d } =  7;
    • if ( a==0 && b==1 && c == 1 && d==1 ) z=1; es equivalente a hacer if ( { a, b, c, d } == 4'b0111 ) z = 1;
    • if ( a==b && c==d ) z=0; es equivalente a hacer if ( { a, b } == { c, d  } ) z = 0;
  14. El último concepto nuevo que vimos en esta clase es el uso de datos compuestos de más de un bit.
    • Hasta el momento los únicos datos que hemos manejado, tanto "reg" como "wire" son datos de un solo bit.
    • Sin embargo, es posible, al declarar un dato, preceder el nombre del dato con un una expresión del tipo "[x:y]". Esto hace que el dato en cuestión sea un dato de varios bits en lugar de un solo bit y que se pueda trabajar con esos bits del dato tanto globalmente como de forma individual.
      • Un ejemplo de esto sería declarar la salida Z de un módulo como output reg [3:0] Z
      • De esta forma estamos diciendo que Z tiene 4 bits. El bit 3 de Z sería el de mayor peso de los 4 y el bit 0 sería el de menor peso.
      • Una sentencia procedimental del tipo Z = 4'b1000; estaría asignando el valor 1 al bit de mayor peso de Z y un 0 a los otros tres bits.
      • Las entencia Z=4'b1000; es equivalente a la sentencia Z[3:0]=4'b1000;
      • La sentencia Z[3] = 0; estaría asignando valor 0 al bit más significativo de Z sin modificar los anteriores.
      • La sentencia Z[1:0] = 2'b11, estaría poniendo a valor 1 los dos bits menos significativos de Z.
      • La sentencia if ( Z[3] == Z[0] ) Z[3:2] = Z[1:0]; asigna a los dos bits más significativos Z el valor de los dos bits menos significativos de Z cuando se cumple que el más significativo de los 4 es igual al menos significativo de los 4.
      • etc...
  15. Hay que recalcar que aunque los bloques "always" tienen el aspecto de un "programa", su misión no es describir los pasos de un algoritmo capaz de hacer una determinada tarea, sino que su objetivo es describir el comportamiento de un circuito. Ya veremos en las prácticas de laboratorio cómo las descripciones de circuitos hechas en Verilog, procesadas por las herramientas software adecuadas, dan lugar a la implementación de un circuito físico real que se comporta del modo especificado en la descripción Verilog.
Acciones de Documento