{josuah.net} | {panoramix-labs.fr}
 (DIR)  • {josuah.net}
 (DIR)  • {panoramix-labs.fr}
       
        {git} | {cv} | {links} | {quotes} | {ascii} | {tgtimes} | {gopher} | {mail}
 (DIR)  • {git}
 (BIN)  • {cv}
 (DIR)  • {links}
 (DIR)  • {quotes}
 (DIR)  • {ascii}
 (HTM)  • {tgtimes}
 (DIR)  • {gopher}
       
       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
       SystemVerilog structs as ersatz to interafces
       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 (HTM)  It happen that interfaces are not well supported by {yosys}.
       
        The syntax is recognised, but the wiring does not happen, and interfaces passed
        to modules let many signals disconnected..
       
        The real solution is to look at yosys source and get a hang of what is
        happening, to patch it.
       
        The alternative to bundle signals together are structs.
       
        They have the inconvenient to not permit input and output signals, nor to allow
        parameters.
       
        So in practices, multiple structs are to be populated, while a single interface
        would have been sufficient.
       
        Below is a comparison.
       
       Interfaces
       ──────────
        Interfaces are more verbose for declaration, but less for each instance (more
        efforts permitting to deduplicate more)
       
        Declaration:
       
        ┊ interface wishbone (
        ┊   input logic clk,
        ┊   input logic rst
        ┊ );
        ┊   logic stb;
        ┊   logic we;
        ┊   logic ack;
        ┊   logic[3:0] adr;
        ┊   logic[7:0] dat_c;
        ┊   logic[7:0] dat_p;
        ┊ 
        ┊   modport peri (
        ┊     input stb,
        ┊     input we, 
        ┊     output ack,
        ┊     input adr,
        ┊     output dat_p,
        ┊     input dat_c
        ┊   );
        ┊ 
        ┊   modport ctrl (
        ┊     output stb,
        ┊     output we, 
        ┊     input ack,
        ┊     output adr,
        ┊     input dat_p,
        ┊     output dat_c
        ┊   );
        ┊ endinterface
       
        Instantiation:
       
        ┊   ...
        ┊   wishbone wb ();
        ┊ 
        ┊   peri p3 (
        ┊     .wb(wb.peri)
        ┊     .gpio_led
        ┊   );
        ┊   ...
        ┊ 
        ┊   module peri (
        ┊     wishbone_ctrl_t wb,
        ┊     output logic gpio_led
        ┊   );
       
       Structs
       ───────
        Structs are less verbose to declare, but more verbose to instanciate: lower
        cost, lower gain
       
        Declaration:
       
        ┊ typedef struct packed {
        ┊   logic ack;
        ┊   logic[7:0] dat;
        ┊ } wishbone_peri_t;
        ┊ 
        ┊ typedef struct packed {
        ┊   logic stb;
        ┊   logic we;
        ┊   logic[3:0] adr;
        ┊   logic[7:0] dat;
        ┊ } wishbone_ctrl_t;
       
        Instantiation:
       
        ┊   ...
        ┊   wishbone_peri_t wb_p;  // this is not two interfaces, one for peripheral,
        ┊   wishbone_ctrl_t wb_c;  // and one for controller, but a same interface with
        ┊         // signals coming from the peripheral or controller
        ┊   peri p3 (
        ┊     .clk, .rst, .wb_p .wb_c
        ┊     .gpio_led
        ┊   );
        ┊   ...
        ┊ 
        ┊   module peri (
        ┊     input logic clk,
        ┊     input logic rst,
        ┊     input wishbone_ctrl_t wb_c,
        ┊     output wishbone_peri_t wb_p,
        ┊     output logic gpio_led
        ┊   );
        ┊   ...
        ┊ endmodule
       
       Naming signals
       ──────────────
        How to keep it together with the directions (aka, RX/TX problem in UART, or
        COPI/CIPO in SPI):
       
        Signals coming from the peripheral:
       
        ┊   wishbone_peri_t wb_p;
       
        Signals coming from the controller:
       
        ┊   wishbone_ctrl_t wb_c;
       
        From here, the naming becomes rather intuitive and natural:
       
        ┊   // the wishbone peripheral's acknoledgement is assigned from
        ┊   // the controller's strobe signal
        ┊   assign wb_p.ack = wb_c.stb; 
        ┊ 
        ┊   // if the wishbone controller's strobe signal goes high
        ┊   if (wb_c.stb) begin
        ┊     // set the duty cycle to the wishbone controller's data
        ┊     duty_cycle <= wb_c.dat;
        ┊   end
        ┊ 
       
        For choosing for where to find `ack` between `wb_p` (peripheral) and `wb_c`
        (controller), the question to ask is: "who acknoledges"?
       
        The peripheral acknoledges, so the `ack` signal belongs to the `wb_p`
        (peripheral) struct.
       
        It is also not needed to add a suffix to the data pins, such as MISO/MOSI or
        CIPO/COPI for SPI, or DAT_I/DAT_O for Wishbone, as the struct already contains
        the direction of the signal:
       
        It becomes: `spi_p.dat`, `wb_p.dat`, `spi_c.dat`, `wb_c.dat`.
       
        Using the source of the signal also means it is never needed to write
        `.dat_i(dat_o)`: the data always go in from somewhere toward somewhere. By
        naming the destination, it tells more about where is that signal coming from
        and what content it has, and the signals names are the same through the whole
        design.
       
        For UART, though, TX and RX make sense, as the connexion is purely symmetrical,
        and there is no controller or peripheral device, or master and slave device.
       
        In that case, it is possible to name them according to the specific use-case.
        For instance `data_rs485_chip` for the data coming from an external RS-485
        transceiver, and `data_sensor` for data coming from the sensor peripheral