¿Cómo hacer referencia a subconjuntos de lógica [31: 0] en SystemVerilog?

2

(Tengo dos preguntas para ti al final.)

Estoy usando SystemVerilog para hacer varios ejercicios (para edificación personal) en el capítulo 7 de Diseño digital y arquitectura de computadora . Estoy usando Quartus II 13.1.2 Web Edition de Altera y ModelSim Altera Starter Edition 10.1d (Revisión 2012.11 con fecha 2 de noviembre de 2012) para el software en Windows 7 x64, y mi hardware no es relevante en este momento (aunque es un Altera DE2-115).

Una cosa que tengo que hacer es decodificar una señal logic de 32 bits en varios campos diferentes para poder decodificar instrucciones. Hay tres posibilidades de decodificación separadas para las instrucciones MIPS de 32 bits que utiliza el libro. Entonces, hice packed struct s por separado para cada uno:

typedef struct packed {
  logic [5:0] op;
  logic [4:0] rs, rt, rd, shamt;
  logic [5:0] funct;
} instruction_r;

typedef struct packed {
  logic [5:0] op;
  logic [4:0] rs, rt;
  logic [15:0] imm;
} instruction_i;

typedef struct packed {
  logic [5:0] op;
  logic [25:0] addr;
} instruction_j;

Cuando intenté convertirlos en union , descubrí que Quartus II no admite union s. Drat.

Entonces, hice otro typedef y usé un poco de casting para hacer que todos fueran iguales, solo que con diferentes nombres:

typedef logic [31:0] instruction;

// usage in a module...

  instruction logic_instr;
  instruction_r instr;
  instruction_i instr_i;
  instruction_j instr_j;

  assign instr = logic_instr;
  assign instr_i = instruction_i'(instr);
  assign instr_j = instruction_j'(instr);

Esto funciona tanto en Quartus II como en ModelSim.

Sin embargo, la necesidad del tipo instruction adicional fue para solucionar un problema que ModelSim me dio y que no molestó a Quartus II. En este siguiente recorte, la primera declaración funciona en ambos, pero la segunda solo funciona en Quartus II.

  // Works in both
  floper #(32) instruction_reg(clk, reset, c_irwrite, readdata, logic_instr);

  // Works only in Quartus II (and allows me to remove the logic_instr entirely)
  floper #(32) instruction_reg(clk, reset, c_irwrite, readdata, instruction'(instr));

// For reference: Enabled, resettable flipflop
module floper #(parameter WIDTH = 8)
               (input  logic             clk, reset, en,
                input  logic [WIDTH-1:0] d, 
                output logic [WIDTH-1:0] q);
  // ...
endmodule

El error que da ModelSim es "(vsim-3053) Salida ilegal o conexión de puerto de entrada para el" puerto 'q' ". No da el error en el momento de la compilación, solo el tiempo de simulación.

Mis preguntas para StackExchange son:

  1. ¿Existe una forma más elegante de tener una variable logic [31:0] a la que se pueda hacer referencia fácilmente por varios subconjuntos de sus cables sin pasar por todo este rigamarole verboso?

  2. ¿Existe una sintaxis que pueda usar con Quartus II y ModelSim que me permita evitar tener otra variable adicional al enviar el struct instruction_r (o i o j ) a un módulo que espera un logic [31:0] ?

Estoy seguro de que tiene que haber una mejor manera de hacer este tipo de cosas, ya que parece algo que probablemente sea muy común en cualquier tipo de diseño a gran escala.

¡Gracias!

    

1 respuesta

1
  
  1. ¿Existe una forma más elegante de tener una variable lógica [31: 0] a la que se pueda hacer referencia fácilmente por varios subconjuntos de sus cables sin pasar por todo este rigamarole detallado?
  2.   
  3. ¿Existe una sintaxis que pueda usar con Quartus II y ModelSim que me permita evitar tener otra variable adicional cuando envío la instrucción struct instruction_r (o i o j) a un módulo que espera una lógica [31: 0]?
  4.   

La respuesta a ambas es sí. Los tipos de datos simples como logic [31:0] y struct packed {...} pueden asignarse directamente; El casting no es necesario, especialmente si son del mismo ancho.

Dado que todo es del mismo tamaño (32 bits), se permite la asignación directa ( example1 ):

instruction_r instr_r;
instruction_i instr_i;
instruction_j instr_j;

floper #(32) instruction_reg_r(.en(c_irwrite), .d(readdata), .q(instr_r), .* );

always_comb begin
  instr_i = instr_r;
  instr_j = instr_r;
end

Incluso se pueden usar los corchetes ( {} ) ( example2 ):

logic [5:0] op;
logic [25:0] addr;
logic [4:0] rs, rt;
logic [15:0] imm;
logic [4:0] rd, shamt;
logic [5:0] funct;

floper #(32) instruction_reg_r(.en(c_irwrite), .d(readdata), .q({op,addr}), .* );

always_comb begin
  {rs,rt,rd,shamt,funct} = addr;
  imm = {rd,shamt,funct};
end

Otro enfoque es utilizar parámetros de tipo. Consulte IEEE Std 1800-2012 § 6.20.3 Tipo de parámetros . En este caso, cambie la definición de floper a:

module floper #(parameter WIDTH = 8, parameter type INSTR_TYPE = logic[WIDTH-1:0] )
               (input  logic             clk, reset, en,
                input  logic [WIDTH-1:0] d, 
                output INSTR_TYPE        q);
  // ...
endmodule

Luego actualice el valor del parámetro en las instancias de floper . example3 :

instruction_r instr_r;
instruction_i instr_i;
instruction_j instr_j;

floper #(32,instruction_r) instruction_reg_r(.en(c_irwrite), .d(readdata), .q(instr_r), .* );
floper #(32,instruction_i) instruction_reg_i(.en(c_irwrite), .d(readdata), .q(instr_i), .* );
floper #(32,instruction_j) instruction_reg_j(.en(c_irwrite), .d(readdata), .q(instr_j), .* );

Los operadores de transmisión ( {<<{}} y {>>{}} ) son necesarios al traducir entre valores empaquetados / no empaquetados, consulte IEEE Std 1800-2012 § 11.4.14 Operadores de transmisión , la conversión se requiere para las asignaciones que no tienen una relación de 1 a 1. La fundición también se puede utilizar para el tuncado o el relleno, lo que es útil para evitar advertencias de desajuste de tamaño. IEEE Std 1800-2012 § 6.24 Casting una descripción completa y ejemplos .

    
respondido por el Greg

Lea otras preguntas en las etiquetas