Fading Coder

An Old Coder’s Final Dance

Home > Tech > Content

AHB‑Lite Single‑Transfer, No‑Wait Verilog Implementation with One Master and Four Slaves

Tech 2

Overview

This example implements a minimal AHB‑Lite–style interconnect that supports single, non‑burst transfers with no wait states. The system contains one master and four simple memory‑mapped slaves. Because there is only one master, no arbiter is required. An address decoder generates one‑hot select signals for the slaves, and a response multiplexer forwards the selected slave’s response back to the master.

Notes:

  • Slave selection uses the top two address bits (addr[31:30]) to choose one of four slaves.
  • Each slave exposes a small 32×32 memory; the lower five bits (addr[4:0]) index the local memory.
  • The master asserts an address/control phase for one cycle, then performs a single READ or WRITE data phase.

Key signals used in this simplified design:

  • haddr: address from master to slaves
  • hwrite: 1 for write, 0 for read
  • hvalid: master indicates "address/control valid" (setup phase)
  • hwdata/hrdata: write data/read data
  • hreadyout: slave response; held at 1 in this no‑wait model (still modeled as a handshake)
  • hsel: per‑slave select from decoder

Top‑Level Interconnect

The top module wires up the master, four slaves, the address decoder, and the response multiplexer.

module ahb_sys (
  input  wire        hclk,
  input  wire        hresetn,

  // Simple host-side control
  input  wire        start,      // trigger a single transfer
  input  wire        wr,         // 1=write, 0=read
  input  wire [31:0] addr,
  input  wire [31:0] din,
  output wire [31:0] dout        // readback for observation
);

  // Bus wires
  wire [31:0] haddr;
  wire        hwrite;
  wire        hvalid;
  wire [31:0] hwdata;

  wire [31:0] hrdata;
  wire        hreadyout;

  // Slave selects and responses
  wire [3:0]  hsel;
  wire [1:0]  sel_idx;

  wire [31:0] hrdata_s [3:0];
  wire        hreadyout_s [3:0];

  // Master
  ahb_master_lite u_master (
    .hclk      (hclk),
    .hresetn   (hresetn),
    .start     (start),
    .addr_i    (addr),
    .wr_i      (wr),
    .data_i    (din),
    .hrdata_i  (hrdata),
    .hready_i  (hreadyout),

    .haddr_o   (haddr),
    .hwrite_o  (hwrite),
    .hvalid_o  (hvalid),
    .hwdata_o  (hwdata),
    .data_o    (dout)
  );

  // Four identical slaves
  ahb_periph u_s0 (
    .hclk      (hclk),
    .hresetn   (hresetn),
    .hsel      (hsel[0]),
    .haddr     (haddr),
    .hwrite    (hwrite),
    .hvalid    (hvalid),
    .hwdata    (hwdata),
    .hreadyout (hreadyout_s[0]),
    .hrdata    (hrdata_s[0])
  );

  ahb_periph u_s1 (
    .hclk      (hclk),
    .hresetn   (hresetn),
    .hsel      (hsel[1]),
    .haddr     (haddr),
    .hwrite    (hwrite),
    .hvalid    (hvalid),
    .hwdata    (hwdata),
    .hreadyout (hreadyout_s[1]),
    .hrdata    (hrdata_s[1])
  );

  ahb_periph u_s2 (
    .hclk      (hclk),
    .hresetn   (hresetn),
    .hsel      (hsel[2]),
    .haddr     (haddr),
    .hwrite    (hwrite),
    .hvalid    (hvalid),
    .hwdata    (hwdata),
    .hreadyout (hreadyout_s[2]),
    .hrdata    (hrdata_s[2])
  );

  ahb_periph u_s3 (
    .hclk      (hclk),
    .hresetn   (hresetn),
    .hsel      (hsel[3]),
    .haddr     (haddr),
    .hwrite    (hwrite),
    .hvalid    (hvalid),
    .hwdata    (hwdata),
    .hreadyout (hreadyout_s[3]),
    .hrdata    (hrdata_s[3])
  );

  // Address decoder (uses top-level addr for selection timing simplicity)
  ahb_decoder2x4 u_dec (
    .addr    (addr),
    .hsel    (hsel),
    .sel_idx (sel_idx)
  );

  // Response MUX
  ahb_resp_mux u_mux (
    .sel_idx      (sel_idx),
    .hrdata_s0    (hrdata_s[0]),
    .hrdata_s1    (hrdata_s[1]),
    .hrdata_s2    (hrdata_s[2]),
    .hrdata_s3    (hrdata_s[3]),
    .hreadyout_s0 (hreadyout_s[0]),
    .hreadyout_s1 (hreadyout_s[1]),
    .hreadyout_s2 (hreadyout_s[2]),
    .hreadyout_s3 (hreadyout_s[3]),
    .hrdata       (hrdata),
    .hreadyout    (hreadyout)
  );
endmodule

Master

A small FSM sequences through SETUP (address/control) and ACCESS (read/write) for a single trasnfer.

module ahb_master_lite (
  input  wire        hclk,
  input  wire        hresetn,

  input  wire        start,       // kick a transfer
  input  wire [31:0] addr_i,
  input  wire        wr_i,
  input  wire [31:0] data_i,

  input  wire [31:0] hrdata_i,
  input  wire        hready_i,

  output reg  [31:0] haddr_o,
  output reg         hwrite_o,
  output reg         hvalid_o,
  output reg  [31:0] hwdata_o,
  output reg  [31:0] data_o
);

  localparam S_IDLE   = 2'd0;
  localparam S_SETUP  = 2'd1;
  localparam S_WRITE  = 2'd2;
  localparam S_READ   = 2'd3;

  reg [1:0] state, nstate;

  // State register
  always @(posedge hclk or negedge hresetn) begin
    if (!hresetn)
      state <= S_IDLE;
    else
      state <= nstate;
  end

  // Next-state logic
  always @* begin
    nstate = state;
    case (state)
      S_IDLE:  nstate = start ? S_SETUP : S_IDLE;
      S_SETUP: nstate = wr_i ? S_WRITE : S_READ;
      S_WRITE: nstate = hready_i ? S_IDLE : S_WRITE;
      S_READ:  nstate = hready_i ? S_IDLE : S_READ;
      default: nstate = S_IDLE;
    endcase
  end

  // Outputs
  always @(posedge hclk or negedge hresetn) begin
    if (!hresetn) begin
      haddr_o  <= 32'h0;
      hwrite_o <= 1'b0;
      hvalid_o <= 1'b0;
      hwdata_o <= 32'h0;
      data_o   <= 32'h0;
    end else begin
      case (state)
        S_IDLE: begin
          haddr_o  <= 32'h0;
          hwrite_o <= 1'b0;
          hvalid_o <= 1'b0;
          hwdata_o <= 32'h0;
          data_o   <= 32'h0;
        end
        S_SETUP: begin
          haddr_o  <= addr_i;
          hwrite_o <= wr_i;
          hvalid_o <= 1'b1;      // address/control valid for one cycle
          hwdata_o <= 32'h0;
        end
        S_WRITE: begin
          haddr_o  <= 32'h0;
          hwrite_o <= 1'b0;
          hvalid_o <= 1'b0;
          hwdata_o <= data_i;    // place write data
        end
        S_READ: begin
          haddr_o  <= 32'h0;
          hwrite_o <= 1'b0;
          hvalid_o <= 1'b0;
          hwdata_o <= 32'h0;
          if (hready_i)
            data_o <= hrdata_i;  // capture read data
        end
        default: begin
          haddr_o  <= 32'h0;
          hwrite_o <= 1'b0;
          hvalid_o <= 1'b0;
          hwdata_o <= 32'h0;
          data_o   <= 32'h0;
        end
      endcase
    end
  end
endmodule

Slave (Memory‑Mapped Peripheral)

Each slave implements a tiny 32×32 register file. Address/control are sampled in SETUP; the ACCESS phase iether writes the memory or returns its contents.

module ahb_periph (
  input  wire        hclk,
  input  wire        hresetn,

  input  wire        hsel,
  input  wire [31:0] haddr,
  input  wire        hwrite,
  input  wire        hvalid,   // master’s address/control valid (setup)
  input  wire [31:0] hwdata,

  output reg         hreadyout,
  output reg  [31:0] hrdata
);

  // 32 x 32-bit memory
  reg [31:0] mem [0:31];
  reg [4:0]  idx_r;

  localparam ST_IDLE  = 2'd0;
  localparam ST_SETUP = 2'd1;
  localparam ST_WACC  = 2'd2;
  localparam ST_RACC  = 2'd3;

  reg [1:0] state, nstate;

  // FSM
  always @(posedge hclk or negedge hresetn) begin
    if (!hresetn)
      state <= ST_IDLE;
    else
      state <= nstate;
  end

  always @* begin
    nstate = state;
    case (state)
      ST_IDLE:  nstate = (hsel) ? ST_SETUP : ST_IDLE;
      ST_SETUP: begin
        if (hvalid)       nstate = (hwrite ? ST_WACC : ST_RACC);
        else              nstate = ST_SETUP;
      end
      ST_WACC:  nstate = ST_IDLE;  // single beat
      ST_RACC:  nstate = ST_IDLE;  // single beat
      default:  nstate = ST_IDLE;
    endcase
  end

  // Outputs / storage
  always @(posedge hclk or negedge hresetn) begin
    if (!hresetn) begin
      hreadyout <= 1'b0;
      hrdata    <= 32'h0;
      idx_r     <= 5'd0;
    end else begin
      case (state)
        ST_IDLE: begin
          hreadyout <= 1'b0;
          hrdata    <= 32'h0;
        end
        ST_SETUP: begin
          hreadyout <= 1'b0;
          // latch the local index from address LSBs
          if (hvalid)
            idx_r <= haddr[4:0];
        end
        ST_WACC: begin
          mem[idx_r] <= hwdata;
          hreadyout  <= 1'b1;   // ready immediately in no-wait model
        end
        ST_RACC: begin
          hrdata    <= mem[idx_r];
          hreadyout <= 1'b1;
        end
        default: begin
          hreadyout <= 1'b0;
          hrdata    <= 32'h0;
        end
      endcase
    end
  end
endmodule

Address Decoder (2‑to‑4)

Generates one‑hot HSEL and an index used by the response mux based on addr[31:30].

module ahb_decoder2x4 (
  input  wire [31:0] addr,
  output reg  [3:0]  hsel,
  output wire [1:0]  sel_idx
);
  assign sel_idx = addr[31:30];

  always @* begin
    case (sel_idx)
      2'b00: hsel = 4'b0001;
      2'b01: hsel = 4'b0010;
      2'b10: hsel = 4'b0100;
      2'b11: hsel = 4'b1000;
      default: hsel = 4'b0000;
    endcase
  end
endmodule

Response Multilpexer

Selects HRDATA and HREADYOUT from the addressed slave.

module ahb_resp_mux (
  input  wire [1:0]  sel_idx,
  input  wire [31:0] hrdata_s0,
  input  wire [31:0] hrdata_s1,
  input  wire [31:0] hrdata_s2,
  input  wire [31:0] hrdata_s3,
  input  wire        hreadyout_s0,
  input  wire        hreadyout_s1,
  input  wire        hreadyout_s2,
  input  wire        hreadyout_s3,
  output reg  [31:0] hrdata,
  output reg         hreadyout
);
  always @* begin
    case (sel_idx)
      2'b00: begin hrdata = hrdata_s0; hreadyout = hreadyout_s0; end
      2'b01: begin hrdata = hrdata_s1; hreadyout = hreadyout_s1; end
      2'b10: begin hrdata = hrdata_s2; hreadyout = hreadyout_s2; end
      2'b11: begin hrdata = hrdata_s3; hreadyout = hreadyout_s3; end
      default: begin hrdata = 32'h0; hreadyout = 1'b0; end
    endcase
  end
endmodule

Testbench

The testbench writes the values 1, 2, 3, 4 to four distinct slave locations, reads each back immediate, then re‑reads all four addresses.

`timescale 1ns/1ns

module tb_ahb_sys;
  reg         hclk;
  reg         hresetn;
  reg         start;
  reg         wr;
  reg  [31:0] addr;
  reg  [31:0] din;
  wire [31:0] dout;

  // DUT
  ahb_sys dut (
    .hclk    (hclk),
    .hresetn (hresetn),
    .start   (start),
    .wr      (wr),
    .addr    (addr),
    .din     (din),
    .dout    (dout)
  );

  // Clock
  initial hclk = 1'b0;
  always #2 hclk = ~hclk;  // 250 MHz

  // Simple tasks (single-beat transfers)
  task do_write(input [31:0] a, input [31:0] v);
  begin
    @(posedge hclk);
    addr  <= a;
    din   <= v;
    wr    <= 1'b1;
    start <= 1'b1;    // setup phase
    @(posedge hclk);
    start <= 1'b0;    // access phase (write data driven by master)
    @(posedge hclk);
    wr    <= 1'b0;
  end
  endtask

  task do_read(input [31:0] a);
  begin
    @(posedge hclk);
    addr  <= a;
    wr    <= 1'b0;
    start <= 1'b1;    // setup phase
    @(posedge hclk);
    start <= 1'b0;    // access phase (slave returns data)
    @(posedge hclk);
  end
  endtask

  initial begin
    hresetn = 1'b0;
    start   = 1'b0;
    wr      = 1'b0;
    addr    = 32'h0;
    din     = 32'h0;

    #20 hresetn = 1'b1;

    // Writes to four slaves (addr[31:30] selects slave), unique word offsets
    do_write(32'h0000_0000, 32'd1);  // S0
    do_read (32'h0000_0000);

    do_write(32'h4000_0004, 32'd2);  // S1
    do_read (32'h4000_0004);

    do_write(32'h8000_0008, 32'd3);  // S2
    do_read (32'h8000_0008);

    do_write(32'hC000_000C, 32'd4);  // S3
    do_read (32'hC000_000C);

    // Final sweep of all addresses
    do_read (32'h0000_0000);
    do_read (32'h4000_0004);
    do_read (32'h8000_0008);
    do_read (32'hC000_000C);

    #20 $finish;
  end
endmodule

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.