Ready-to-run snippets

Code Examples

Ready-to-run Verilog, SystemVerilog, and UVM examples. Click any to open it live in the IDE.

← All examples

Address Decoder with Aliasing (bug)

Verilog Bus Protocols

The teaching bus with a partial address decoder: the memory also answers at unmapped addresses. Fix the decoder.

Open in IDE
design.v
// A small teaching bus used in the first chapters of the Bus Protocols tutorial.
// One master, a decoder and two slaves. A transfer is presented with sel = 1 and
// completes in the cycle in which ready = 1 (sampled at the rising clock edge).
//   sel    : a transfer is requested
//   we     : 1 = write, 0 = read
//   addr   : 8-bit byte address
//   wdata  : write data
//   rdata  : read data, valid when ready = 1 on a read
//   ready  : the slave completes the transfer in this cycle
//   err    : the transfer failed (valid when ready = 1)

// Slave 0: four 8-bit registers, no wait states
module regs_slave (
  input            clk,
  input            rst,
  input            sel,
  input            we,
  input      [1:0] addr,
  input      [7:0] wdata,
  output     [7:0] rdata,
  output           ready
);
  reg [7:0] r [0:3];
  integer   i;

  always @(posedge clk) begin
    if (rst) begin
      for (i = 0; i < 4; i = i + 1)
        r[i] <= 8'h00;
    end
    else if (sel && we) begin
      r[addr] <= wdata;
    end
  end

  assign rdata = r[addr];
  assign ready = 1'b1;      // always ready: every transfer takes one cycle
endmodule

// Slave 1: a 16-byte memory with one wait state
module mem_slave (
  input            clk,
  input            rst,
  input            sel,
  input            we,
  input      [3:0] addr,
  input      [7:0] wdata,
  output     [7:0] rdata,
  output           ready
);
  reg [7:0] m [0:15];
  reg       waited;         // 1 in the second cycle of a transfer

  always @(posedge clk) begin
    if (rst)
      waited <= 1'b0;
    else if (sel && !waited)
      waited <= 1'b1;
    else
      waited <= 1'b0;
  end

  assign ready = waited;    // first cycle: wait, second cycle: done

  always @(posedge clk) begin
    if (sel && we && ready)
      m[addr] <= wdata;
  end

  assign rdata = m[addr];
endmodule

// Decoder and read multiplexer:
// 0x00-0x0F registers, 0x10-0x1F memory, anything else is an error
module sbus_system (
  input            clk,
  input            rst,
  input            sel,
  input            we,
  input      [7:0] addr,
  input      [7:0] wdata,
  output     [7:0] rdata,
  output           ready,
  output           err
);
  wire       hit_regs = (addr[7:4] == 4'h0);
  wire       hit_mem  = addr[4];                  // bug: only bit 4 is decoded
  wire [7:0] rd_regs, rd_mem;
  wire       rdy_regs, rdy_mem;

  regs_slave u_regs (
    .clk   (clk),
    .rst   (rst),
    .sel   (sel && hit_regs),
    .we    (we),
    .addr  (addr[1:0]),
    .wdata (wdata),
    .rdata (rd_regs),
    .ready (rdy_regs)
  );

  mem_slave u_mem (
    .clk   (clk),
    .rst   (rst),
    .sel   (sel && hit_mem),
    .we    (we),
    .addr  (addr[3:0]),
    .wdata (wdata),
    .rdata (rd_mem),
    .ready (rdy_mem)
  );

  assign rdata = hit_regs ? rd_regs  :
                 hit_mem  ? rd_mem   : 8'h00;
  assign ready = hit_regs ? rdy_regs :
                 hit_mem  ? rdy_mem  : 1'b1;     // unmapped: complete at once ...
  assign err   = sel && !hit_regs && !hit_mem;   // ... with an error
endmodule
testbench.v
// Testbench: acts as the bus master with a write task and a read task
module tb;
  reg        clk   = 0;
  reg        rst   = 1;
  reg        sel   = 0;
  reg        we    = 0;
  reg  [7:0] addr  = 0;
  reg  [7:0] wdata = 0;
  wire [7:0] rdata;
  wire       ready;
  wire       err;

  sbus_system dut (
    .clk   (clk),
    .rst   (rst),
    .sel   (sel),
    .we    (we),
    .addr  (addr),
    .wdata (wdata),
    .rdata (rdata),
    .ready (ready),
    .err   (err)
  );

  always #5 clk = ~clk;

  task bus_write(input [7:0] a, input [7:0] d);
    begin
      @(negedge clk);
      sel   = 1;
      we    = 1;
      addr  = a;
      wdata = d;
      @(posedge clk);
      while (!ready)                // wait until the slave completes
        @(posedge clk);
      $display("%0t  write addr=%h data=%h %s", $time, a, d, err ? "ERROR" : "ok");
      @(negedge clk);
      sel = 0;
      we  = 0;
    end
  endtask

  task bus_read(input [7:0] a);
    begin
      @(negedge clk);
      sel  = 1;
      we   = 0;
      addr = a;
      @(posedge clk);
      while (!ready)
        @(posedge clk);
      $display("%0t  read  addr=%h data=%h %s", $time, a, rdata, err ? "ERROR" : "ok");
      @(negedge clk);
      sel = 0;
    end
  endtask

  initial begin
    $dumpfile("dump.vcd");
    $dumpvars(0, tb);
    #12 rst = 0;
    bus_write(8'h01, 8'hA5);        // register 1
    bus_read (8'h01);
    bus_write(8'h13, 8'h3C);        // memory byte 3: one wait state
    bus_read (8'h13);
    bus_read (8'h53);               // not in the memory map: should be an error
    #20 $finish;
  end
endmodule
Admin mode — edit and delete controls are visible on each row. Use the IDE to add new snippets via File → Save as Snippet.