Ik ben sinds kort weer eens met mijn Cyclone III starterkit aan't klooien gegaan en ben tegen een aantal zaken aangelopen. Aangezien mijn project niet meer het knipperen van ledjes is maar deze keer een flink stuk groter (retro video processor) ben ik aan't proberen dit project te partitioneren, dus mijn source files aan het opsplitten, hierarchie aanbrengen etc.

Met standaard Verilog liep ik al gauw tegen het probleem op dat ik nogal veel aan het kloppen was en veel parameters op meerdere plekken moest gaan definieren wat ik een beetje zonde vind. Toen ben ik eens gaan kijken wat System Verilog nou te bieden heeft en ik moet zeggen dat dat in veel aspecten dingen een aanzienlijk stuk makkelijker maken. De interface construct is echt 'king' om het zo te zeggen.

Maar waar ik nog steeds niet achter ben is hoe ik constanten kan declareren die als parameters gebruikt kunnen het worden.

Het geval: Ik ben nu bezig met de S-RAM controller. Niet dat een S-RAM nou een echte controller nodig heeft, maar in dit project gaan meerdere stukken logica van de S-RAM gebruik maken. Dus ik heb er een controller om heen gemaakt die enerzijds direct met de S-RAM interfaced, en anderzijds 4 channels welke toegang kunnen aanvragen via de controller. Dus het is eigenlijk een bus arbiter.

Zowel de databreedte van de S-RAM databus als de databreedte van de 4 channels wil ik generiek houden, dus die heb ik via parameter declaraties gedefinieerd. Maar ik moet die parameters op zowel de S-RAM interface declareren, op de Arbiter module en op de Channel interface. Ik vind dat nou niet echt geweldig.

Ik zoek dus eigenlijk een methode om in het verilogbestand éénmalig een constate te definieren om in 1 keer de databreedte voor de 3 design units in te stellen.

Iemand een idee hoe ik dat het beste kan oplossen? (Kan jij me wijzer maken Free_electron?)

P.S. ja, de controller/arbiter werkt al wel. De SRAM op het starterkit bordje kan tot 200MHz en met het juist aanbrengen van constraints in TimeQuest heb ik 't al voorelkaar gekregen om de S-RAM ook daadwerkelijk op die snelheid te laten lopen :)

Hier de controller source, kun je precies zien wat ik bedoel:

/* =======================================================================================
 * Title:     4 Channel S-RAM Controller / Arbiter
 * Device:    ISSI Pipelined Synchronous RAM
 * FPGA:      Cyclone III
 * =======================================================================================
 * Units:     RamBus          S-RAM addres-, data- and control signal interface
 *            RamChannel      Controller Channel interface
 *            RamControl      Controller module
 * =======================================================================================
 * This module implements a controller for ISSI pipelined S-RAM devices. The 
 * controller adds two pipeline stages to facilitate fast input/output registers
 * for optimal timing. The S-RAM is interfaced through the RAM interface.
 *
 * Interfacing with the S-RAM takes place through the controller's channel interfaces 
 * (CHx interfaces). Channel 0 has the highest priority, channel 3 the lowest.
 * 
 * A channel requests access through its 2-bit CHx.REQ port using the following constants:
 *
 * Constant  | 1 | 0
 * ----------+---+---
 * RAM_NOP   | 0 | x
 * RAM_READ  | 1 | 0
 * RAM_WRITE | 1 | 1
 *
 * When the controller grants access to the channel, it asserts CHx.ACK.
 * 
 * When a channel is granted access, it should provide the address for the operation
 * through its CHx.A port. In case of a write operation, the data to be written 
 * must be provided through the CHx.D port. 
 
 * When a read operation has been granted, data will become available a number of 
 * clocks afterwards. When data is ready, the controller asserts the CHx.DR port for
 * the channel that invoked the corresponding read operation and the CHx.Q port will
 * present the data.
 *
 * The S-RAM controller needs a dedicated PLL output which feeds the S-RAM
 * clock. This clock can then be phase shifted to meet S-RAM interface I/O
 * constraints. The PLL clock is repeated through a DDIO element for optimal timing.
 * ======================================================================================= */

/* =======================================================================================
 * S-RAM Bus
 * ======================================================================================= */
 
interface RamPins(

  /* Bus signals */
  inout   logic [Wd - 1:0]    DQ,                   //  Bidirectional data bus
  output  logic [Wa - 1:0]    A,                    //  Address bus
  output  logic [Wb - 1:0]    BEn,                  //  Byte enable
  output  logic               WEn,                  //  Write enable
  output  logic               OEn,                  //  Output enable
  output  logic               CEn,                  //  Chip enable
  output  logic               CLK,                  //  Clock  
  output  logic               ADSCn);               //  Burstmode select

  /* Bus widths */
  parameter Wd  = 32;                               //  Databus width
  parameter Wa  = 18;                               //  Addressbus width
  parameter Wb  = 4;                                //  Byte width

endinterface

/* =======================================================================================
 * Channel Bus
 * ======================================================================================= */
 
interface RamChannel(

  /* System signals */
  input   logic               CLK);                 // System Clock

  /* Master / Slave port definitions */
  modport M(input  A, D, BE, REQ, output Q, ACK, DR);
  modport S(output A, D, BE, REQ, input  Q, ACK, DR);
  
          /* Channel signals */
          logic [Wa - 1:0]    A;                    //  Address
          logic [Wd - 1:0]    D;                    //  Data-in  (Write)
          logic [Wd - 1:0]    Q;                    //  Data-out (Read)
          logic [Wb - 1:0]    BE;                   //  Byte Enable
          logic [1:0]         REQ;                  //  Access Request
          logic               ACK;                  //  Access Acknowledge
          logic               DR;                   //  Data-ready
  
  /* Channel Tasks */


  /* Bus widths */
  parameter Wd  = 32;                               //  Databus width
  parameter Wa  = 18;                               //  Addressbus width
  parameter Wb  = 4;                                //  Byte width

endinterface

/* =======================================================================================
 * Controller
 * ======================================================================================= */
 
module RamControl(

  /* System signals */
  input   logic               RST,                  //  Asynchronous reset
  input   logic               CLK,                  //  System Clock
  input   logic               PLL,                  //  S-RAM Dedicated PLL clock
                                          
          /* S-RAM Interface */
          RamPins             RAM,                  //  S-RAM Pins
        
          /* Channel Interfaces */
          RamChannel.M        CH0,                  //  Channel 0
          RamChannel.M        CH1,                  //  Channel 0
          RamChannel.M        CH2,                  //  Channel 0
          RamChannel.M        CH3);                 //  Channel 0
       
          /* Internal S-RAM control registers */
          logic [Wa - 1:0]    si_a;                 //  Buffered Address
          logic [Wd - 1:0]    si_d;                 //  Buffered Data-in        
          logic               si_ce;                //  Buffered Chip Enable
          logic [Wb - 1:0]    si_be;                //  Buffered Byte Enable
          logic               si_we;                //  Buffered Write Enable
          logic               si_drp  [0:Trd + 3];  //  Data-ready Pipeline
        
          /* Request / Channel decoding */
          logic [1:0]         ch_cs;                //  Channel Select
          logic [1:0]         ch_csp  [0:Trd + 3];  //  Channel Select Pipeline
          logic               ch_req;               //  Channel Request Active
          logic [Wa - 1:0]    ch_a;                 //  MUX'ed Channel Address
          logic [Wd - 1:0]    ch_d;                 //  MUX'ed Channel Data-in
          logic [Wd - 1:0]    ch_q;                 //  MUX'ed Channel Data-in
          logic [Wb - 1:0]    ch_be;                //  MUX'ed Channel Byte Enable
          logic               ch_os;                //  MUX'ed Channel Operation Select
          logic               ch_dr;                //  Shortcut to data-ready pipeline
        
          /* State control */
          logic [1:0]         request;              //  Request code
          logic               block;                //  Block Write during read
       
  /* variables */
  integer index;

  /* Parameters (Cyclone III Starter Kit default settings) */
  parameter Trd       = 0;
  parameter Wd        = 32;
  parameter Wa        = 18;
  parameter Wb        = 4;

  /* Request codes */
  parameter REQ_NONE  = 2'b0x;
  parameter REQ_READ  = 2'b10;
  parameter REQ_WRITE = 2'b11;

  /* -------------------------------------------------------------------------------------
   * Clock Repeater
   * ------------------------------------------------------------------------------------- */
 
  altddio_out ClockRepeater ( .outclock   ( PLL     ),
                              .datain_h   ( 1'b1    ),
                              .aclr       ( RST     ),
                              .datain_l   ( 1'b0    ),
                              .dataout    ( RAM.CLK ),
                              .aset       ( 1'b0    ),
                              .oe         ( 1'b1    ),
                              .oe_out     (         ),
                              .outclocken ( 1'b1    ),
                              .sclr       ( 1'b0    ),
                              .sset       ( 1'b0    ));
  defparam
    ClockRepeater.extend_oe_disable      = "UNUSED",
    ClockRepeater.intended_device_family = "Cyclone III",
    ClockRepeater.invert_output          = "OFF",
    ClockRepeater.lpm_type               = "altddio_out",
    ClockRepeater.oe_reg                 = "UNUSED",
    ClockRepeater.width                  = 1;

  /* -------------------------------------------------------------------------------------
   * Combinatorial
   * ------------------------------------------------------------------------------------- */

  always_comb
  begin
    /* S-RAM Signal Assignments */
    RAM.A     <=  si_a;
    RAM.DQ    <=  si_we ? si_d : {Wd{1'bz}};
    RAM.CEn   <= ~si_ce;
    RAM.BEn   <= ~si_be;
    RAM.WEn   <= ~si_we;
    RAM.OEn   <= ~si_drp[Trd + 1];
    RAM.ADSCn <=  0;
    /* Channel defaults */
    CH0.ACK   <=  0;
    CH1.ACK   <=  0;
    CH2.ACK   <=  0;
    CH3.ACK   <=  0;
    /* Internal defaults */
    ch_req    <=  1;
    ch_cs     <=  0;
    ch_a      <=  CH0.A;
    ch_d      <=  CH0.D;
    ch_be     <=  CH0.BE;
    ch_os     <=  CH0.REQ[0];
    /* Channel Request / Select encoder */
    casex ( { CH0.REQ[1], CH1.REQ[1], CH2.REQ[1], CH3.REQ[1] } )
      4'b1xxx: begin
                 /* Channel 0 */
                 CH0.ACK <= !(CH0.REQ[0] && block);
               end
      4'b01xx: begin
                 /* Channel 1 */
                 ch_cs   <=   1; 
                 ch_a    <=   CH1.A; 
                 ch_d    <=   CH1.D; 
                 ch_be   <=   CH1.BE; 
                 ch_os   <=   CH1.REQ[0]; 
                 CH1.ACK <= !(CH1.REQ[0] && block);
               end
      4'b001x: begin
                 /* Channel 2 */
                 ch_cs   <=   2;
                 ch_a    <=   CH2.A;
                 ch_d    <=   CH2.D;
                 ch_be   <=   CH2.BE;
                 ch_os   <=   CH2.REQ[0];
                 CH2.ACK <= !(CH2.REQ[0] && block);
               end
      4'b0001: begin
                 /* Channel 3 */
                 ch_cs   <=   3;
                 ch_a    <=   CH3.A;
                 ch_d    <=   CH3.D;
                 ch_be   <=   CH3.BE;
                 ch_os   <=   CH3.REQ[0];
                 CH3.ACK <= !(CH3.REQ[0] && block);
               end
      4'b0000: begin
                 /* No requests, default muxing */
                 ch_req  <=   0;
               end
    endcase
    /* Data-ready shortcut */
    ch_dr     <= si_drp[Trd + 3];
    /* Channel data-ready decoding */  
    CH0.DR    <= ch_dr && (ch_csp[Trd + 3] == 0);
    CH1.DR    <= ch_dr && (ch_csp[Trd + 3] == 1);
    CH2.DR    <= ch_dr && (ch_csp[Trd + 3] == 2);
    CH3.DR    <= ch_dr && (ch_csp[Trd + 3] == 3);
    /* Channel data output */
    CH0.Q     <= ch_q;
    CH1.Q     <= ch_q;
    CH2.Q     <= ch_q;
    CH3.Q     <= ch_q;
    /* State control */
    request   <= { ch_req, ch_os };
    block     <= si_drp[Trd + 0] || si_drp[Trd + 1];
  end

  /* -------------------------------------------------------------------------------------
   * Controller state machine
   * ------------------------------------------------------------------------------------- */
 
  always_ff @ (posedge CLK, posedge RST) 
  begin
    if (RST) 
      Clear;
    else
    begin
      /* State defaults */
      si_a      <= ch_a;
      si_d      <= ch_d;
      si_be     <= ch_be;
      si_drp[0] <= 0;
      ch_csp[0] <= ch_cs;
      ch_q      <= RAM.DQ;
      /* State processing */
      casex (request)
        REQ_NONE:  begin
                     si_we     <= 0;
                     si_ce     <= si_drp[Trd];
                   end
        REQ_WRITE: begin
                     si_we     <= !block;
                     si_ce     <= 1;
                   end
        REQ_READ:  begin
                     si_we     <= 0;
                     si_ce     <= 1;
                     si_drp[0] <= 1;
                   end
      endcase
      /* Pipeline shifting */
      for (index = Trd + 3; index > 0; index = index - 1)
      begin
        si_drp[index] <= si_drp[index - 1];
        ch_csp[index] <= ch_csp[index - 1];
      end
    end
  end

  /* -------------------------------------------------------------------------------------
   * Tasks
   * ------------------------------------------------------------------------------------- */
 
  task Clear;
    /* Port registers */
    ch_q  <= 0;
    /* Internal Registers */
    si_a  <= 0;
    si_d  <= 0;
    si_be <= 0;
    si_we <= 0;
    si_ce <= 0;
    /* Pipelined Registers */
    for (index = 0; index < Trd + 4; index = index + 1)
    begin
      si_drp[index] <= 0;
      ch_csp[index] <= 0;
    end
  endtask

  /* ------------------------------------------------------------------------------------- */

endmodule

Het gaat dus om de parameters Wd, Wa en Wb. Ook zou'k nog willen weten hoe ik in deze file constanten kan definieren voor de 3 requests, NOP, READ en WRITE request als aangegeven in de omschrijving, zodat ik die dan kan gebruiken in aanroepende modules.

Kerel toch. Constantes op meerdere plaatsen definieren. Tsk, tsk.. Had maar eerst VHDL geleerd :-p

Maar goed. Constantes in 1 enkele file zetten, en die include-en in elke file waar je ze nodig hebt. In VHDL doe je dat met een package, die je als lib in je file opneemt. In Verilog is het gewoon een include file. Niks geen SysVerilog voor nodig.
Overigens, het is altijd beter om eerst even na te denken over je architectuur en zo de verdeling over de (sub)blokken te doen. Achteraf is dat altijd vele malen lastiger.

Oh, en met die constanten kun je natuulijk je busbreedtes schaalbaar maken, maar dat had je al begrepen geloof ik.

[Bericht gewijzigd door flipflop op (10%)]

constanten op toplevel zetten en meegeven in de aanroep van de module. (net zoals je ene parameter meegeeft )
de syntax moewt ik opzoeken. mijn tplevels zijn altijd schema :) en paramters gebruik ik niet. ik werk altijd met een tros ' `defines '

[Bericht gewijzigd door free_electron op (56%)]

Volgensmij kan ik het met 'defparam' statements op top-nivo doen op elke instance, dat lijkt ook te werken. Goed, ik moet dat wel voor elke instance doen, maar op top-nivo kennik dan eenmalig de constante definieren en met defparam met die waarde de individuele instances zetten:


parameter RamWidth = 32;

defparam
  ramCtrl.Wd = RamWidth;
  ram.Wd     = RamWidth;
  ch0.Wd     = RamWidth;
  ch1.Wd     = RamWidth;

Voor mijn gevoel nog steeds wel een beetje omslachtig maar het brengt op zijn minst wat duidelijkheid als dat defparam blokje alleen op het topnivo zit onderaan de file of zo.

Met include files krijg ik errors over duplicate namen, maar da's waarschijnlijk omdat Quartus alle bestanden al 'included' dus zelf een include neerzetten zou dubbelop zijn.

Die dubbele files krijg je alleen als je de included file ook in je project opneemt, of meegeeft in de aanroep naar NCSIM/VerilogXL (simulator). Dat hoeft dus niet als je ze include in een sourcefile. Include doet niets ander dan op die plaats de include file plakken.

ps, in Verilog heet het natuurlijk geen constant zoals ik hierboven schreef, maar 'define zoals f_e schreef. What's in a name.

Na nog meer doorlezen en bij al het 'nieuws' bedenken hoe'k het zou kunnen gebruiken, dan toch de oplossing voor mijn wens gevonden :)

SystemVerilog enumerations (net als in C, C# etc etc):


typedef enum bit [1:0] {
  REQ_NONE  = 2'b00,                //  Request not desired
  REQ_READ  = 2'b10,                //  Request a read
  REQ_WRITE = 2'b11                 //  Request a write
} RamRequest;

Werkt ideal, is veel netter om een iets als CH0.REQ <= REQ_READ; te schrijven.

Ook structs zijn een geweldige uitkomst om signalen te bundelen en modulepoorten overzichtelijker te houden :)

Gebruiken jullie (FE en/of flipflop) deze 'features' ook in je dagelijkse HDL?

Uiteraard gebruik ik deze features. Elke dag. Voor statemachines zijn enums ideaal. State coderen als IDLE ipv "000" (bv). Structs oid zijn ook handig om signalen te bundelen die bij elkaar horen. Wel uitkijken dat je geen enorm brede bussen krijgt omdat je niet meer zo direct ziet hoeveel signalen er in zo'n struct zitten.

Maar ehm, geen SystemVerilog hoor. Gewoon VHDL :-)