Skip to content

VHDL · Chapter 13.4 · Advanced Data Structures

Records as Bus Interfaces

A real bus has a dozen signals, such as address, write data, read data, valid, ready, and strobes, and wiring them individually through a hierarchy is tedious and error-prone. The production fix is to bundle the bus into a record and pass that one typed port between modules. The long port list collapses to a single interface, both sides use the same definition from a package, and adding a field propagates everywhere automatically. The one real subtlety is direction. A plain record flows a single way, but a bus is bidirectional, so the standard pattern splits it into two records, a request bundle from master to slave carrying address and write data, and a response bundle from slave to master carrying read data and ready. This lesson shows how to model bus interfaces with records, the request and response split, and why this makes interconnect maintainable.

Foundation14 min readVHDLRecordsBusInterfacesConnectivityReuse

1. Engineering intuition — wire the bus once, as one thing

Every extra loose signal in a port list is another chance to mis-wire, mis-order, or forget one — and a bus is many signals that always travel together. So treat the bus as one object: a record that bundles all its fields. Now a module has a single bus port, the top level connects it with a single signal, and there is no way to transpose addr and wdata because they live inside one named structure. The only thing you must respect is that a bus goes both ways — commands out, data back — and a single record only flows one way. The clean answer is two records: one for each direction.

2. Formal explanation — request/response records for direction

bus_records_pkg.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library ieee; use ieee.std_logic_1164.all;
package bus_pkg is
  -- REQUEST bundle: everything that flows MASTER → SLAVE.
  type bus_req_t is record
    addr  : std_logic_vector(15 downto 0);
    wdata : std_logic_vector(31 downto 0);
    write : std_logic;
    valid : std_logic;
  end record;
  -- RESPONSE bundle: everything that flows SLAVE → MASTER.
  type bus_rsp_t is record
    rdata : std_logic_vector(31 downto 0);
    ready : std_logic;
  end record;
end package;
 
-- Each direction is a simple (one-way) record → modes are clean:
--   master: req is OUT, rsp is IN ;  slave: req is IN, rsp is OUT.

A bus is split into a request record (master→slave) and a response record (slave→master), each declared once in a package. Because each record now flows a single direction, the in/out port modes are unambiguous on both sides. (VHDL-2008 adds record mode views to express mixed directions within one record, but the request/response split is the portable, widely-used idiom.)

3. Production usage — one interface, both sides, the top wires two signals

record_bus_in_use.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
library work; use work.bus_pkg.all;
library ieee; use ieee.std_logic_1164.all;
-- Master and slave share the SAME interface types; ports collapse to one each per direction.
entity master is port ( clk : in std_logic; req : out bus_req_t; rsp : in  bus_rsp_t ); end entity;
entity slave  is port ( clk : in std_logic; req : in  bus_req_t; rsp : out bus_rsp_t ); end entity;
 
architecture top of system is
  signal req : bus_req_t;          -- one signal carries the whole request bundle
  signal rsp : bus_rsp_t;          -- one signal carries the whole response bundle
begin
  u_m : entity work.master port map (clk => clk, req => req, rsp => rsp);
  u_s : entity work.slave  port map (clk => clk, req => req, rsp => rsp);
  -- Inside a module, fields are ordinary signals: req.addr <= a;  if req.valid='1' then ...
end architecture;

What hardware does this become? Exactly the same wires as listing every bus signal by hand — the record is a source-level grouping, so req/rsp synthesize to the individual addr/wdata/rdata/ready nets. The win is at design time: two ports instead of a dozen, no field can be mis-wired, and adding a field (say a byte strobe) to bus_req_t updates master, slave, and every connection consistently — change one type, not ten port lists. This is why production interconnect is built from record interfaces.

4. Structural interpretation — request and response records

master and slave connected by a request record one way and a response record the other waydrivesmaster→slavedrivesslave→mastermasterreq OUT, rsp INbus_req_taddr, wdata, write, validbus_rsp_trdata, readyslavereq IN, rsp OUT12
A bus modeled as two one-way records solves the direction problem. The request record (addr, wdata, write, valid) flows master to slave; the response record (rdata, ready) flows slave to master. Each is declared once in a package and used by both sides, so port lists collapse to one port per direction and adding a field propagates everywhere. Because each record is single-direction, the in/out port modes are unambiguous. The record is a source-level grouping that synthesizes to the same individual wires. This is a connectivity structure, captured by a diagram rather than a waveform.

5. Why this is structural, not timing

A record bus interface is a connectivity/source structure — it bundles the same wires under named fields and splits direction into request/response — so the structural diagram above tells the story, not a waveform. At run time the fields behave exactly like the individual bus signals they group (req.valid is just a std_logic, req.addr a vector), with whatever protocol timing the bus already has. The value is entirely at design time: fewer ports, no mis-wiring, one place to evolve the interface — a maintainability gain in the netlist's structure, not a change in behaviour.

6. Debugging example — one record, both directions (the mode clash)

Expected: a clean bidirectional bus interface. Observed: a port-mode error, or read data that cannot be driven back, when trying to put both directions in a single record port. Root cause: a simple record flows one way, but the design crammed master-driven and slave-driven fields into one record with a single in/ out mode — so one side cannot drive its fields. Fix: split the bus into two records — a request bundle (master→slave) and a response bundle (slave→master) — each with a consistent direction and clean modes (or use a VHDL-2008 record mode view if your tool supports it). Engineering takeaway: never force both directions into one record port; model a bidirectional bus as request and response records so every field has an unambiguous direction.

split_directions.vhd
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-- BUG: one record with both directions → which way does rdata go? mode clash.
-- type bus_t is record addr, wdata, rdata, ... ; end record;  port ( bus : inout bus_t );
-- FIX: two one-way records, clean modes on each side.
port ( req : out bus_req_t; rsp : in bus_rsp_t );   -- master side; slave reverses them

7. Common mistakes & what to watch for

  • Both directions in one record. A simple record is one-way; split into request/response records (or use 2008 mode views) so modes are clean.
  • Declaring the interface per module. Define the record types once in a package; duplicated definitions drift apart.
  • Forgetting fields propagate. Adding a field updates everyone using the type — intended, but check all drivers set it (e.g. a new strobe).
  • Over-bundling. Group signals that genuinely belong to one interface/direction; do not stuff unrelated signals into a bus record.
  • Resolution/driver issues. Each field still follows driver rules; do not multiply-drive a response field from several slaves without arbitration.

8. Engineering insight & continuity

Records as bus interfaces are the production pattern for clean interconnect: bundle a bus into one typed port, share the definition from a package, and let added fields propagate everywhere — solving direction with a request record (master→slave) and a response record (slave→master) so every field's mode is unambiguous. The result is fewer ports, no mis-wiring, and one place to evolve the interface. With composite data structures (arrays of records, multidimensional/unconstrained arrays, record interfaces) in hand, the next lesson applies them to storage in depth — Memory and RAM/ROM Modeling — the coding styles that infer block RAM, distributed RAM, and ROM.