📄 apb.v
字号:
// ________________________________________________________________ apb.v ___
//
// Interface model for ARM APB (AMBA Peripheral Bus),
// used as an example of inter-module communication
// with interfaces.
//
// The bus is quite simple and can have only one master.
// The master can perform read and write cycles to one location
// at a time (no burst transfers). The bus is fully synchronous
// to the rising edges of an externally supplied clock.
//
// Note that APB doesn't specify a global reset signal, so any reset
// required by devices on the bus must be supplied separately and
// doesn't form part of this interface.
//
// The interface also offers procedural entry points so that
// a test fixture can perform read and write cycles without
// needing to create a bus functional model of the bus master.
// Unfortunately, VCS 7.1 doesn't yet allow us to pass these
// procedural entry points through a modport; we have to
// invoke them using hierarchical names, just as with a
// typical Verilog bus functional model (BFM).
//
//
// Revision information:
// =====================
//
// v0.0 19-Jan-2004 Jonathan Bromley
// Modified from tci_bus.v
//
// v0.1 25-Jan-2004 Jonathan Bromley
// Improved internal documentation
//
// __________________________________________________________________________
`timescale 1ns/100ps
// _________________________________________________________ GLOBAL STUFF ___
// Publicly visible typedefs for APB data and address types:
//
typedef logic [15:0] T_APB_d;
typedef logic [15:0] T_APB_a;
// __________________________________________________________________________
// _________________________________________________ INTERFACE DEFINITION ___
interface APB #(parameter master_Tco = 1);
// See also modports and tasks:
//
// modport RTL_slave -- connect any RTL slave model to one of these
// modport RTL_master -- connect an RTL master model to this (one only)
// modport TF_master -- Connect a behavioural master to this (one only)
// task read; -- BFM entry point for behavioural tests
// task write; -- BFM entry point for behavioural tests
// __________________________________________________________________________
// _________________________________________________ PHYSICAL BUS SIGNALS ___
// Bus clock, generated by external master (typically AHB-APB bridge).
//
// NOTE: Even if there is no external master, but only a testbench using
// the BFM read and write entry points, it is ESSENTIAL for the
// environment to provide a suitable clock on this signal.
// This can be done either directly or through the RTL_master modport.
//
logic PCLK;
// Select signal. Each slave must qualify this signal
// with the appropriate address, since we can't
// parameterise the slave's modport.
//
logic psel = 0;
// Address signal from master
//
T_APB_a PADDR;
// Write data, from master
//
T_APB_d PWDATA;
// Write and enable signals from master
//
logic PWRITE = 0;
logic PENABLE = 0;
// Readback data signal, written by selected slave
//
T_APB_d PRDATA;
// __________________________________________________________________________
// _____________________________________________________________ MODPORTS ___
// Any slave connects like this...
//
modport RTL_slave (
input PCLK, // Slaves get their clock from the bus
//input psel, // Select signal - global for all slaves
input PWRITE, // Active in the clock when address is sent
input PENABLE, // Direction control, sent with the address
input PADDR,
input PWDATA,
output PRDATA
); // modport RTL_slave
// The one and only master connects like this:
//
modport RTL_master (
output PCLK, // Master supplies clock to the bus
output PWRITE, // Active in the clock when address is sent
output PENABLE, // Direction control, sent with the address
output PADDR,
output PWDATA,
input PRDATA
); // modport RTL_master
// Alternatively you can connect a behavioural master
// to this BFM-like modport:
//
modport TF_master (
output PCLK, // Master supplies clock to the bus
import task write(), read() // Master calls these tasks to do cycles
); // modport TF_master
// __________________________________________________________________________
// _____________________________________________________ BFM ENTRY POINTS ___
// Non-synthesisable task-call interface for a testbench master
// so that it can exercise slaves on the bus without the need for
// a fully-functional model of a master device.
// Call read and write tasks at the posedge of the clock
// right at the beginning of the cycle. They return just after
// the final posedge of the cycle.
task read (
input T_APB_a adrs,
output T_APB_d data
);
time start_delay;
CheckLastClock(start_delay);
#(start_delay)
PADDR = adrs;
PWRITE = 1'b0;
psel = 1'b1;
@(posedge PCLK)
PENABLE <= #master_Tco 1'b1;
@(posedge PCLK)
data = PRDATA;
PADDR <= {$bits(PADDR){1'bx}};
psel <= 1'b0;
PENABLE <= 1'b0;
endtask // read
task write (
input T_APB_a adrs,
input T_APB_d data
);
time start_delay;
CheckLastClock(start_delay);
#(start_delay)
PADDR = adrs;
PWDATA = data;
PWRITE = 1'b1;
psel = 1'b1;
@(posedge PCLK)
PENABLE <= #master_Tco 1'b1;
@(posedge PCLK)
PWDATA <= {$bits(PADDR){1'bx}};
PADDR <= {$bits(PADDR){1'bx}};
psel <= 1'b0;
PENABLE <= 1'b0;
endtask // write
// __________________________________________________________________________
// ____________________________________________ HELPER CODE FOR BFM TASKS ___
// As always with procedural entry points into a BFM, there is
// an issue about synchronisation between the moment of task call
// and the bus's clock. If the task is called at some random time,
// its activity must be resynchronised to the bus clock. However,
// if it's already synchronised, then we don't want a spurious
// additional clock wait. One possible solution to this problem is
// offered here: the BFM read and write tasks can be called at any
// time. If called at or just after a clock edge, BFM activity can
// proceed as from that clock edge (signals don't need to be set up
// until one clock-to-valid delay after the clock edge). If called
// at any other time, the task is stalled until just after the next
// clock edge.
// In order to make this approach work, we need to keep track of
// the time of the most recent active clock edge.
//
always @(posedge PCLK) begin : ClockEdgeLogger
time LastClock;
LastClock = $time;
end // ClockEdgeLogger
// Now, when a BFM task is called, it uses the time-of-last-clock
// information to determine whether it can proceed immediately, or
// must wait until the next clock. Task checkLastClock() encapsulates
// this behaviour; it not only enforces the clock wait if necessary,
// but also calculates the future time at which the BFM task must
// apply its valid bus signals.
//
// Note that this task will still work correctly even if
// parameter master_Tco is set to zero.
//
task CheckLastClock (output time t);
// If this task was called as the result of a clock edge,
// we must be sure that the LastClock value is updated.
// That's why we need the #0 delay prefix here.
//
#0 t = $time - ClockEdgeLogger.LastClock;
// Are we too late to start the bus cycle on the current clock?
//
if (t > master_Tco) begin
// Yes, we're too late. Wait for the next clock.
@(posedge PCLK) t = master_Tco;
end else begin
// No, we're in time. Calculate how long we need to wait
// before applying the right signals.
t = master_Tco - t;
end
endtask
// __________________________________________________________________________
endinterface // APB
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -