\subsubsection{CBus} \index{CBus@\te{CBus} (package)} \label{lib-cbus} {\bf Package} \begin{verbatim} import CBus :: * ; \end{verbatim} {\bf Description} The \te{CBus} package provides the interface, types and modules to implement a configuration bus capability providing access to the control and status registers in a given module hierarchy. This package utilizes the \te{ModuleCollect} package and functionality, as described in section \ref{package-modulecollect}. The \te{ModuleCollect} package allows items in addition to usual state elements and rules to be accumulated. This is required to collect up the interfaces of the control status registers included in a module and to add the associated logic and ports required to allow them to be accessed via a configuration bus. %For a more complete discussion of the \te{CBus} %package, consult the configbus tutorial in the BSV/tutorials directory. {\bf Types and Type Classes} The type \te{CBusItem} defines the type of item to be collected by \te{ModuleCollect}. The items to be collected are the same as the ifc which we will later expose, so we use a type alias: \begin{libverbatim} typedef CBus#(size_address, size_data) CBusItem #(type size_address, type size_data); \end{libverbatim} The type \te{ModWithCBus} defines the type of module which is collecting \te{CBusItem}s. An ordinary module, one not collecting anything other than state elements and rules, has the type \te{Module}. Since \te{CBusItem}s are being collected, a module type \te{ModWithCBus} is defined. When the module type is not \te{Module}, the type must be specified in square brackets immediately after the \te{module} keyword in the module definition. \begin{libverbatim} typedef ModuleCollect#(CBusItem#(size_address, size_data), item) ModWithCBus#(type size_address, type size_data, type item); \end{libverbatim} % typedef struct { % Bit#(size_address) a; % Bit#(size_address) o; % } CRAddr#(numeric type size_address) deriving(Bits, Eq); {\bf Interface and Methods} \index{CBus@\te{CBus} (interface)} \index{IWithCBus@\te{IWithCBus} (interface)} The \te{CBus} interface provides \te{read} and \te{write} methods to access control status registers. It is polymorphic in terms of the size of the address bus (\te{size\_address}) and size of the data bus (\te{size\_data}). %defines the interface for the configuration bus (the {\em back door} interface). \begin{center} \begin{tabular}{|p{.6 in}|p{4 in}|} \hline \multicolumn{2}{|c|}{\te{CBus} Interface}\\ \hline Name & Description\\ \hline \hline &\\ \te{write}&Writes the \te{data} value to the register if and only if the value of \te{addr} matches the address of the register.\\ \hline &\\ \te{read}&Returns the value of the associated register if and only if \te{addr} matches the register address. In all other cases the \te{read} method returns an \te{Invalid} value.\\ \hline \end{tabular} \end{center} \begin{libverbatim} interface CBus#(type size_address, type size_data); method Action write(Bit#(size_address) addr, Bit#(size_data) data); (* always_ready *) method ActionValue#(Bit#(size_data)) read(Bit#(size_address) addr); endinterface \end{libverbatim} The \te{IWithCBus} interface combines the \te{CBus} interface with a normal module interface. It is defined as a structured interface with two subinterfaces: \te{cbus\_ifc} (the associated configuration bus interface) and \te{device\_ifc} (the associated device interface). It is polymorphic in terms of the type of the configuation bus interface and the type of the device interface. %the device interface (the {\em front door} interface). \begin{libverbatim} interface IWithCBus#(type cbus_IFC, type device_IFC); interface cbus_IFC cbus_ifc; interface device_IFC device_ifc; endinterface \end{libverbatim} {\bf Modules} \index{collectCBusIFC@\te{collectCBusIFC} (module)} \index[function]{CBus!collectCBusIFC} The \te{collectCBusIFC} module takes as an argument a module with an \te{IWithCBus} interface, adds the associated \te{CBus} interface to the current collection (using \te{addToCollection} from the \te{ModuleCollect} package), and returns a module with the normal interface. Note that \te{collectCBusIFC} is of module type \te{ModWithCBus}. \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{collectCBusIFC}& Adds the \te{CBus} to the collection and returns a module with just the device interface.\\ \cline{2-2} &\begin{libverbatim} module [ModWithCBus#(size_address, size_data)] collectCBusIFC#(Module#(IWithCBus#( CBus#(size_address,size_data),i)) m)(i); \end{libverbatim} \\ \hline \end{tabular} \end{center} \index{exposeCBusIFC@\te{exposeCBusIFC} (module)} \index[function]{CBus!exposeCBusIFC} The \te{exposeCBusIFC} module is used to create an \te{IWithCBus} interface given a module with a normal interface and an associated collection of \te{CBusItem}s. This module takes as an argument a module of type \te{ModWithCBus} and provides an interface of type \te{IWithCBus}. The \te{exposeCBusIFC} module exposes the collected \te{CBusItem}s, processes them, and provides a new combined interface. This module is synthesizable, because it is of type \te{Module}. \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{exposeCBusIFC}& A module wrapper that takes a module with a normal interface, processes the collected CBusItems and provides an IWithCBus interface.\\ \cline{2-2} &\begin{libverbatim} module [Module] exposeCBusIFC#(ModWithCBus#( size_address, size_data, item) sm) (IWithCBus#(CBus#(size_address, size_data), item)); \end{libverbatim} \\ \hline \end{tabular} \end{center} The \te{CBus} package provides a set of module primitives each of which adds a \te{CBus} interface to the collection and provides a normal \te{Reg} interface from the local block point of view. These modules are used in designs where a normal register would be used, and can be read and written to as registers from within the design. \index{mkCBRegR@\te{mkCBRegR} (module)} \index[function]{CBus!mkCBRegR} \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{mkCBRegR}&A wrapper to provide a read only \te{CBus} interface to the collection and a normal \te{Reg} interface to the local block.\\ \cline{2-2} &\begin{libverbatim} module [ModWithCBus#(size_address, size_data)] mkCBRegR#(CRAddr#(size_address2) addr, r x) (Reg#(r)) provisos (Bits#(r, sr), Add#(k, sr, size_data), Add#(ignore, size_address2, size_address)); \end{libverbatim} \\ \hline \end{tabular} \end{center} \index{mkCBRegRW@\te{mkCBRegRW} (module)} \index[function]{CBus!mkCBRegRW} \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{mkCBRegRW}&A wrapper to provide a read/write \te{CBus} interface to the collection and a normal \te{Reg} interface to the local block.\\ \cline{2-2} &\begin{libverbatim} module [ModWithCBus#(size_address, size_data)] mkCBRegRW#(CRAddr#(size_address2) addr, r x) (Reg#(r)) provisos (Bits#(r, sr), Add#(k, sr, size_data), Add#(ignore, size_address2, size_address)); \end{libverbatim} \\ \hline \end{tabular} \end{center} \index{mkCBRegW@\te{mkCBRegW} (module)} \index[function]{CBus!mkCBRegW} \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{mkCBRegW}&A wrapper to provide a write only \te{CBus} interface to the collection and a normal \te{Reg} interface to the local block.\\ \cline{2-2} &\begin{libverbatim} module [ModWithCBus#(size_address, size_data)] mkCBRegW#(CRAddr#(size_address2) addr, r x) (Reg#(r)) provisos (Bits#(r, sr), Add#(k, sr, size_data), Add#(ignore, size_address2, size_address)); \end{libverbatim} \\ \hline \end{tabular} \end{center} \index{mkCBRegRC@\te{mkCBRegRC} (module)} \index[function]{CBus!mkCBRegRC} \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{mkCBRegRC}&A wrapper to provide a read/clear \te{CBus} interface to the collection and a normal \te{Reg} interface to the local block. This register can read from the config bus but the write is clear mode; for each write bit a 1 means clear, while a 0 means don't clear. \\ \cline{2-2} &\begin{libverbatim} module [ModWithCBus#(size_address, size_data)] mkCBRegRC#(CRAddr#(size_address2) addr, r x) (Reg#(r)) provisos (Bits#(r, sr), Add#(k, sr, size_data), Add#(ignore, size_address2, size_address)); \end{libverbatim} \\ \hline \end{tabular} \end{center} \index{mkCBRegFile@\te{mkCBRegFile} (module)} \index[function]{CBus!mkCBRegFile} The \te{mkCBRegFile} module wrapper adds a \te{CBus} interface to the collection and provides a \te{RegFile} interface to the design. This module is used in designs as a normal \te{RegFile} would be used. \begin{center} \begin{tabular}{|p{1 in}|p{4.65 in}|} \hline &\\ \te{mkCBRegFile}&A wrapper to provide a normal \te{RegFile} interface and automatically add the \te{CBus} interface to the collection. \\ \cline{2-2} &\begin{libverbatim} module [ModWithCBus#(size_address, size_data)] mkCBRegFile#(Bit#(size_address) reg_addr, Bit#(size_address) size) (RegFile#(Bit#(size_address), r)) provisos (Bits#(r, sr), Add#(k, sr, size_data)); \end{libverbatim} \\ \hline \end{tabular} \end{center} {\bf Example} Provided here is a simple example of a CBus implementation. The example is comprised of three packages: \te{CfgDefines}, \te{Block}, and \te{Tb}. The \te{CfgDefines} package contains the definition for the configuration bus, \te{Block} is the design block, and \te{Tb} is the testbench which executes the block. The \te{Block} package contains the local design. As seen in Figure \ref{cbusreg}, the configuration bus registers look like a single field from the CBus (\te{cfgResetAddr, cfgStateAddr, cfgStatusAddr}), while each field (\te{reset, init, cnt}, etc.) in the configuration bus registers looks like a regular register from from the local block point of view. \begin{figure}[ht] \begin{center} \includegraphics[width = 5 in]{LibFig/cbus1} \caption{CBus Registers used in Block example} \label{cbusreg} \end{center} \end{figure} \begin{verbatim} import CBus::*; // this is a BSC library import CfgDefines::*; // user defines - address,registers, etc interface Block; // TODO: normally this block would have at least a few methods // Cbus interface is hidden, but it is there endinterface // In order to access the CBus at this parent, we need to expose the bus. // Only modules of type [Module] can be synthesized. module [Module] mkBlock(IWithCBus#(DCBus, Block)); let ifc <- exposeCBusIFC( mkBlockInternal ); return ifc; endmodule // Within this module the CBus looks like normal Registers. // This module can't be synthesized directly. // How these registers are combined into CBus registers is // defined in the CfgDefines package. module [DModWithCBus] mkBlockInternal( Block ); // all registers are read/write from the local block point of view // config register interface types can be // mkCBRegR -> read only from config bus // mkCBRegRW -> read/write from config bus // mkCBRegW -> write only from config bus // mkCBRegRC -> read from config bus, write is clear mode // i.e. for each bit a 1 means clear, 0 means don't clear // reset bit is write only from config bus // we presume that you use this bit to fire some local rules, etc Reg#(TCfgReset) reg_reset_reset <- mkCBRegW(cfg_reset_reset, 0 /* init val */); Reg#(TCfgInit) reg_setup_init <- mkCBRegRW(cfg_setup_init, 0 /* init val */); Reg#(TCfgTz) reg_setup_tz <- mkCBRegRW(cfg_setup_tz, 0 /* init val */); Reg#(TCfgCnt) reg_setup_cnt <- mkCBRegRW(cfg_setup_cnt, 1 /* init val */); Reg#(TCfgOnes) reg_status_ones <- mkCBRegRC(cfg_status_ones, 0 /* init val */); Reg#(TCfgError) reg_status_error <- mkCBRegRC(cfg_status_error, 0 /* init val */); // USER: you know have registers, so do whatever it is you do with registers :) // for instance rule bumpCounter ( reg_setup_cnt != unpack('1) ); reg_setup_cnt <= reg_setup_cnt + 1; endrule rule watch4ones ( reg_setup_cnt == unpack('1) ); reg_status_ones <= 1; endrule endmodule \end{verbatim} The \te{CfgDefines} package contains the user defines describing how the local registers are combined into the configuration bus. \begin{verbatim} package CfgDefines; import CBus::*; //////////////////////////////////////////////////////////////////////////////// /// basic defines //////////////////////////////////////////////////////////////////////////////// // width of the address bus, it's easiest to use only the width of the bits needed // but you may have other reasons for passing more bits around (even if some address // bits are always 0) typedef 2 DCBusAddrWidth; // roof( log2( number_of_config_registers ) ) // the data bus width is probably defined in your spec typedef 32 DCBusDataWidth; // how wide is the data bus //////////////////////////////////////////////////////////////////////////////// // Define the CBus //////////////////////////////////////////////////////////////////////////////// typedef CBus#( DCBusAddrWidth,DCBusDataWidth) DCBus; typedef CRAddr#(DCBusAddrWidth,DCBusDataWidth) DCAddr; typedef ModWithCBus#(DCBusAddrWidth, DCBusDataWidth, i) DModWithCBus#(type i); //////////////////////////////////////////////////////////////////////////////// /// Configuration Register Types //////////////////////////////////////////////////////////////////////////////// // these are configuration register from your design. The basic // idea is that you want to define types for each individual field // and later on we specify which address and what offset bits these // go to. This means that config register address fields can // actually be split across modules if need be. // typedef bit TCfgReset; typedef Bit#(4) TCfgInit; typedef Bit#(6) TCfgTz; typedef UInt#(8) TCfgCnt; typedef bit TCfgOnes; typedef bit TCfgError; //////////////////////////////////////////////////////////////////////////////// /// configuration bus addresses //////////////////////////////////////////////////////////////////////////////// Bit#(DCBusAddrWidth) cfgResetAddr = 0; // Bit#(DCBusAddrWidth) cfgStateAddr = 1; // Bit#(DCBusAddrWidth) cfgStatusAddr = 2; // maybe you really want this to be 0,4,8 ??? //////////////////////////////////////////////////////////////////////////////// /// Configuration Register Locations //////////////////////////////////////////////////////////////////////////////// // DCAddr is a structure with two fields // DCBusAddrWidth a ; // this is the address // // this does a pure comparison // Bit#(n) o ; // this is the offset that this register // // starts reading and writting at DCAddr cfg_reset_reset = DCAddr {a: cfgResetAddr, o: 0}; // bits 0:0 DCAddr cfg_setup_init = DCAddr {a: cfgStateAddr, o: 0}; // bits 0:0 DCAddr cfg_setup_tz = DCAddr {a: cfgStateAddr, o: 4}; // bits 9:4 DCAddr cfg_setup_cnt = DCAddr {a: cfgStateAddr, o: 16}; // bits 24:16 DCAddr cfg_status_ones = DCAddr {a: cfgStatusAddr, o: 0}; // bits 0:0 DCAddr cfg_status_error = DCAddr {a: cfgStatusAddr, o: 1}; // bits 1:1 //////////////////////////////////////////////////////////////////////////////// /// //////////////////////////////////////////////////////////////////////////////// endpackage \end{verbatim} The \te{Tb} package executes the block. \begin{verbatim} import CBus::*; // bsc library import CfgDefines::*; // address defines, etc import Block::*; // test block with cfg bus import StmtFSM::*; // just for creating a test sequence (* synthesize *) module mkTb (); // In order to access this cfg bus we need to use IWithCBus type IWithCBus#(DCBus,Block) dut <- mkBlock; Stmt test = seq // write the bits need to the proper address // generally this comes from software or some other packing scheme // you can, of course, create functions to pack up several fields // and drive that to bits of the correct width // For that matter, you could have your own shadow config registers // up here in the testbench to do the packing and unpacking for you dut.cbus_ifc.write( cfgResetAddr, unpack('1) ); // put some ones in the status bits dut.cbus_ifc.write( cfgStateAddr, unpack('1) ); // show that only the valid bits get written $display("TOP: state = %x at ", dut.cbus_ifc.read( cfgStateAddr ), $time); // clear out the bits dut.cbus_ifc.write( cfgStateAddr, 0 ); // but the 'ones' bit was set when it saw all ones on the count // so read it to see that... $display("TOP: status = %x at ", dut.cbus_ifc.read( cfgStatusAddr ), $time); // now clear it dut.cbus_ifc.write( cfgStatusAddr, 1 ); // see that it's clear $display("TOP: status = %x at ", dut.cbus_ifc.read( cfgStatusAddr ), $time); // and if we had other interface methods, that where not part of CBUS // we would access them via dut.device_ifc endseq; mkAutoFSM( test ); endmodule \end{verbatim}