\subsubsection{BRAM}
\index{BRAM (package)}
%\index{Xilinx!BRAM (package)}
\label{sec-BRAM}

{\bf Package}


\begin{verbatim}
import BRAM :: * ;
\end{verbatim}


{\bf Description}

The \te{BRAM} package provides types, interfaces, and modules
to support FPGA BlockRams.  The BRAM modules include FIFO wrappers to
provide implicit conditions for proper flow control for the BRAM
latency.    Specific 
tools may determine whether modules are mapped to appropriate BRAM
cells during synthesis.

The low-level modules that implement BRAM without implicit conditions
are available in the \te{BRAMCore} package,
Section \ref{sec-BRAMCore}. Most designs should use the \te{BRAM}
package, \te{BRAMCore} should only be used if you need access to
low-level core BRAM modules without implicit conditions.

{\bf Types and type classes}
\index{BRAM\_Configure@\te{BRAM\_Configure} (type)}
\paragraph{BRAM\_Configure}

The \te{BRAM\_Configure} structure specifies the underlying  modules and their
attributes for instantiation.    Default values for the BRAM are
defined with the \te{DefaultValue} instance and can easily be modified.

\begin{center}
\begin{tabular}{|p {.8 in}|p {.7 in}| p {2.2 in}|p {1.5in}|}
\hline
\multicolumn{4}{|c|}{BRAM\_Configure Structure}\\
\hline
Field &Type &Description& Allowed or\\
&&& Recommended Values\\
\hline
\hline
\te{memorySize}&Integer&Number of words in the BRAM   &\\
\hline
\te{latency}&Integer&Number of stages in the read&1 (address is registered)\\
&&&2 (address and data are registered)\\
\hline
\te{loadFormat}&LoadFormat&Describes the  load file&\te{None}\\
&&&\te{tagged Hex} {\em filename}  \\
&&&\te{tagged Binary} {\em filename}\\
\hline
\te{outFIFODepth}&Integer&The depth of the BypassFIFO after the BRAM
for the BRAMServer module&latency+2 \\
\hline
\multicolumn{2}{|l|}{\te{allowWriteResponseBypass}}&&\\
&Bool&Determines if write responses can directly be enqueued in the
output fifo (\te{latency = 0} for write).&\\
\hline
\end{tabular}
\end{center}

The size of the BRAM is determined by the \te{memorySize} field given
in number of words.  The width of a word is determined by the
polymorphic type \te{data} specified in the BRAM interface. If
the \te{memorySize} field is 0, then  memory size = $ 2^{n}$, where
\te{n} is the number 
of address bits determined from the address type.

The \te{latency} field has two valid values; \te{1} indicates that the
address on the read is registered, \te{2} indicates that both the
address on the read input and the  data on the read output  are
registered.  When latency = 2, the components in the dotted box in
Figure \ref{bram} are included.

The \te{outFIFODepth} is  used to determine the depth of the Bypass
FIFO after the BRAM in  the  \te{mkBRAMServer} module.  This value
should be \te{latency + 2} to allow full pipeline behavior.

The \te{allowWriteResponseBypass} field, when \te{True}, 
specifies that the write response is issued on the same cycle as the
write request.  If \te{False}, the write reponse is pipelined, which is 
 the same behavior as the read request. When \te{True}, the schedule
 constraints between \te{put} and \te{get} are \te{put SBR get}.
 Otherwise, the annotation is \te{get CF put} (no constraint).



\begin{verbatim}
typedef struct {Integer      memorySize ;
                Integer      latency ;              // 1 or 2 can extend to 3
                LoadFormat   loadFormat;            // None, Hex or Binary
                Integer      outFIFODepth;
                Bool         allowWriteResponseBypass;
               } BRAM_Configure ;
\end{verbatim}

The \te{LoadFormat} defines the type of the load file (\te{None},
 \te{Hex} or \te{Binary}). The type \te{None} is used when there is no
 load file.  When the type is \te{Hex} or \te{Binary}, the
 name of the load file is provided as  a \te{String}. 

\begin{verbatim}
typedef union tagged {
                      void None;
                      String Hex;
                      String Binary;
   } LoadFormat
deriving (Eq, Bits);
\end{verbatim}

The default values are defined in this package using the
\te{DefaultValue} instance for \te{BRAM\_Configure}.  You can modify
the default values by changing this instance or by modifying specific
fields in your design.

\begin{center}
\begin{tabular}{|p{1.8 in}|p {.8 in}| p{.5  in}| p {2.2in}|}
\hline
\multicolumn{4}{|c|}{Values defined in defaultValue}\\
\hline
Field&Type& Value&Meaning\\
\hline
\hline
\te{memorySize}&Integer&0& $ 2^{n}$, where \te{n} is the number
of address bits\\
\hline
\te{latency}&Integer&1&address is registered\\
\hline
\te{outFIFODepth}&Integer&3&latency + 2\\
\hline
\te{loadFormat}&\te{LoadFormat}  &None&no load file is used\\
\hline
\te{allowWriteResponseBypass}&Bool&False&the write response is pipelined\\
\hline
\end{tabular}
\end{center}

\begin{verbatim}
instance DefaultValue #(BRAM_Configure);
   defaultValue = BRAM_Configure {memorySize        : 0
                                 ,latency           : 1 // No output reg
                                 ,outFIFODepth      : 3
                                 ,loadFormat        : None
                                 ,allowWriteResponseBypass  : False  };
endinstance
\end{verbatim}

To modify a default configuration for your design, set the field you want to
change to the new value.  Example:
\begin{verbatim}
BRAM_Configure cfg = defaultValue ;     //declare variable cfg 
             cfg.memorySize = 1024*32 ; //new value for memorySize
             cfg.loadFormat = tagged Hex "ram.txt";  //value for loadFormat
BRAM2Port#(UInt#(15), Bit#(16)) bram <- mkBRAM2Server (cfg) ;
                                        //instantiate 32K x 16 bits BRAM module
\end{verbatim}


\index{BRAMRequest@\te{BRAMRequest} (type)}

\paragraph{BRAMRequest}

The \te{BRAM} package defines  2  structures for a BRAM request:
\te{BRAMRequest}, and the byte enabled version \te{BRAMRequestBE}.

\begin{center}
\begin{tabular}{|p {1.2 in}|p {.5 in}| p {3.2 in}|}
\hline
\multicolumn{3}{|c|}{ BRAMRequest Structure}\\
\hline
Field &Type &Description\\
\hline
\hline
\te{write}&Bool&Indicates whether this operation is a  write (\te{True)} or
a read (\te{False}).\\
\hline
\te{responseOnWrite}&Bool&Indicates whether a response should be
received from this write command \\
\hline
\te{address}&\te{addr}&Word address of the read or write\\
\hline
\te{datain}&\te{data}&Data to be written.  This field is ignored for reads.\\
\hline
\end{tabular}
\end{center}

\begin{verbatim}
typedef struct {Bool write;
                Bool responseOnWrite;
                addr address;
                data datain;
               } BRAMRequest#(type addr, type data) deriving(Bits, Eq);
\end{verbatim}



\paragraph{BRAMRequestBE}
The  structure \te{BRAMRequestBE} allows for the byte enable signal.

\begin{center}
\begin{tabular}{|p {1.2 in}|p {.5 in}| p {3.2 in}|}
\hline
\multicolumn{3}{|c|}{ BRAMRequestBE Structure}\\
\hline
Field &Type &Description\\
\hline
\hline
\te{writeen}&\te{Bit\#(n)}&Byte-enable indicating whether this operation is
a write (n != 0) or a read (n = 0).\\
\hline
\te{responseOnWrite}&Bool&Indicates whether a response should be
received from this write command \\
\hline
\te{address}&\te{addr}&Word address of the read or write\\
\hline
\te{datain}&\te{data}&Data to be written.  This field is ignored for reads.\\
\hline
\end{tabular}
\end{center}



\begin{verbatim}
typedef struct {Bit#(n) writeen;
                Bool    responseOnWrite;
                addr    address;
                data    datain;
               } BRAMRequestBE#(type addr, type data, numeric type n) deriving (Bits, Eq);
\end{verbatim}

{\bf Interfaces and Methods}

%\index{BRAM\_PORT@\te{BRAM\_PORT} (interface type)}
\index{BRAM@\te{BRAM} (interface type)}
%\index{BRAM\_DUAL\_PORT@\te{BRAM\_DUAL\_PORT} (interface type)}

The interfaces for the BRAM are built on the \te{Server} interface
defined in the \te{ClientServer} package, Section
\ref{lib-clientserver}.  Some type aliases specific to the BRAM are
defined here.

BRAM Server and Client interface types :
\begin{verbatim}
typedef Server#(BRAMRequest#(addr, data), data) BRAMServer#(type addr, type data);
typedef Client#(BRAMRequest#(addr, data), data) BRAMClient#(type addr, type data);
\end{verbatim}

 Byte-enabled BRAM Server and  Client interface types:
\begin{verbatim}
typedef Server#(BRAMRequestBE#(addr, data, n), data) 
        BRAMServerBE#(type addr, type data, numeric type n);
typedef Client#(BRAMRequestBE#(addr, data, n), data) 
        BRAMClientBE#(type addr, type data, numeric type n);
\end{verbatim}


The \te{BRAM} package defines 1 and 2 port interfaces, with
write-enabled and byte-enabled versions.  Each BRAM  port interface contains a
\te{BRAMServer\#(addr, data)} subinterface and a clear action, which clears
the output FIFO of any pending requests.  The data in the BRAM is not cleared.

\begin{center}
\begin{tabular}{|p{.7 in}|p{2.1 in}|p{2.7 in}|}
\hline
\multicolumn{3}{|c|}{BRAM1Port Interface}\\
\multicolumn{3}{|c|}{1 Port BRAM Interface}\\
\hline
Name & Type&Description \\
\hline
\hline 
\te{portA}&\te{BRAMServer\#(addr, data)} &Server subinterface\\
\hline
\te{portAClear}&Action&Method to clear the portA output FIFO \\
\hline
\end{tabular}
\end{center}

\begin{verbatim}
interface BRAM1Port#(type addr, type data);
   interface BRAMServer#(addr, data) portA;
   method Action portAClear;
endinterface: BRAM1Port
\end{verbatim}

\begin{center}
\begin{tabular}{|p{.7 in}|p{2.1 in}|p{2.7 in}|}
\hline
\multicolumn{3}{|c|}{BRAM1PortBE Interface}\\
\multicolumn{3}{|c|}{Byte enabled 1 port BRAM Interface}\\
\hline
Name & Type&Description \\
\hline
\hline 
\te{portA}&\te{BRAMServerBE\#(addr, data, n)} &Byte-enabled server subinterface\\
\hline
\te{portAClear}&Action&Method to clear the portA output FIFO \\
\hline
\end{tabular}
\end{center}


\begin{verbatim}
interface BRAM1PortBE#(type addr, type data, numeric type n);
   interface BRAMServerBE#(addr, data, n) portA;
   method Action portAClear;
endinterface: BRAM1PortBE
\end{verbatim}

\begin{center}
\begin{tabular}{|p{.7 in}|p{2.1 in}|p{2.7 in}|}
\hline
\multicolumn{3}{|c|}{BRAM2Port Interface}\\
\multicolumn{3}{|c|}{ 2 port BRAM Interface}\\
\hline
Name & Type&Description \\
\hline
\hline 
\te{portA}&\te{BRAMServer\#(addr, data)} &Server subinterface for
port A\\
\hline
\te{portB}&\te{BRAMServer\#(addr, data)} &Server subinterface for port
B\\
\hline
\te{portAClear}&Action&Method to clear  the port A output FIFO \\
\hline
\te{portBClear}&Action&Method to clear the port B output FIFO \\
\hline
\end{tabular}
\end{center}

\begin{verbatim}
interface BRAM2Port#(type addr, type data);
   interface BRAMServer#(addr, data) portA;
   interface BRAMServer#(addr, data) portB;
   method Action portAClear;
   method Action portBClear;
endinterface: BRAM2Port
\end{verbatim}

\begin{center}
\begin{tabular}{|p{.7 in}|p{2.1 in}|p{2.7 in}|}
\hline
\multicolumn{3}{|c|}{BRAM2PortBE Interface}\\
\multicolumn{3}{|c|}{Byte enabled 2 port BRAM Interface}\\
\hline
Name & Type&Description \\
\hline
\hline 
\te{portA}&\te{BRAMServerBE\#(addr, data, n)} &Byte-enabled server
subinterface for port A\\
\hline
\te{portB}&\te{BRAMServerBE\#(addr, data, n)} &Byte-enabled server
subinterface for port B\\
\hline
\te{portAClear}&Action&Method to clear  the  portA output FIFO \\
\hline
\te{portBClear}&Action&Method to clear the portB output FIFO \\
\hline
\end{tabular}
\end{center}

\begin{verbatim}
interface BRAM2PortBE#(type addr, type data, numeric type n);
   interface BRAMServerBE#(addr, data, n) portA;
   interface BRAMServerBE#(addr, data, n) portB;
   method Action portAClear;
   method Action portBClear;
endinterface: BRAM2PortBE
\end{verbatim}



{\bf Modules}

The BRAM modules defined in the \te{BRAMCore} package (Section
\ref{sec-BRAMCore}) are wrapped with control logic to turn the BRAM
into a server, as shown in Figure \ref{bram}.  The BRAM Server modules
include an output FIFO and 
logic to control its loading and to avoid overflow.  A single port,
single clock byte-enabled version is provided as well as 2 port and
dual clock  write-enabled versions.


\begin{figure}[ht]
\begin{center}
\includegraphics[width = 4 in]{LibFig/BRAM}
\caption{1 port of a BRAM Server}
\label{bram}
\end{center}
\end{figure}



\index{mkBRAM1Server@\te{mkBRAM1Server} (module)}
\index[function]{BRAM!mkBRAM1Server}
\label{sec-BRAM1Server}
\begin{tabular}{|p{1.4 in}|p{4.2 in}|}
\hline
& \\
\te{mkBRAM1Server}&BRAM Server module including an output FIFO and
logic to control  loading and to avoid overflow. \\
\cline{2-2}
& \begin{libverbatim}
module mkBRAM1Server #( BRAM_Configure cfg ) 
                      ( BRAM1Port #(addr, data) )
   provisos(Bits#(addr, addr_sz),
            Bits#(data, data_sz),
            DefaultValue#(data) );
\end{libverbatim}
\\
\hline
\end{tabular}
\label{sec-BRAM1ServerBE}
\index{mkBRAM1ServerBE@\te{mkBRAM1ServerBE} (module)}
\index[function]{BRAM!mkBRAM1ServerBE}

\begin{tabular}{|p{1.4 in}|p{4.2 in}|}
\hline
& \\
\te{mkBRAM1ServerBE}&Byte-enabled BRAM Server module. \\
\cline{2-2}
& \begin{libverbatim}
module mkBRAM1ServerBE #( BRAM_Configure cfg ) 
                        ( BRAM1PortBE #(addr, data, n) )
   provisos(Bits#(addr, addr_sz),
            Bits#(data, data_sz),
            Div#(data_sz, n, chunk_sz),
            Mul#(chunk_sz, n, data_sz), 
            DefaultValue#(data) );
\end{libverbatim}
\\
\hline
\end{tabular}


\index{mkBRAM2Server@\te{mkBRAM2Server} (module)}
\index[function]{BRAM!mkBRAM2Server}

\begin{tabular}{|p{1.4 in}|p{4.2 in}|}
\hline
& \\
\te{mkBRAM2Server}&2 port BRAM Server module. \\
\cline{2-2}
& \begin{libverbatim}
module mkBRAM2Server #( BRAM_Configure cfg ) 
                      ( BRAM2Port #(addr, data) )
   provisos(Bits#(addr, addr_sz),
            Bits#(data, data_sz),
            DefaultValue#(data) );
\end{libverbatim}
\\
\hline
\end{tabular}

\begin{tabular}{|p{1.4 in}|p{4.2 in}|}
\hline
& \\
\te{mkBRAM2ServerBE}&Byte-enabled 2 port BRAM Server module. \\
\cline{2-2}
& \begin{libverbatim}
module mkBRAM2ServerBE #( BRAM_Configure cfg ) 
                        ( BRAM2PortBE #(addr, data, n) )
   provisos(Bits#(addr, addr_sz),
            Bits#(data, data_sz),
            Div#(data_sz, n, chunk_sz),
            Mul#(chunk_sz, n, data_sz) );
\end{libverbatim}
\\
\hline
\end{tabular}



\index{mkSyncBRAM2Server@\te{mkSyncBRAM2Server} (module)}
\index[function]{BRAM!mkSyncBRAM2Server}

\begin{tabular}{|p{1.4 in}|p{4.2 in}|}
\hline
& \\
\te{mkSyncBRAM2Server}&2 port, dual clock,  BRAM Server module. The
\te{portA} subinterface and \te{portAClear} methods are in the
\te{clkA} domain;
the \te{portB} subinterface and \te{portBClear} methods are in the \te{clkB}
domain. \\
\cline{2-2}
& \begin{libverbatim}
(* no_default_clock, no_default_reset *)
module mkSyncBRAM2Server #( BRAM_Configure cfg,
                           Clock clkA, Reset rstNA,
                           Clock clkB, Reset rstNB
                           ) (BRAM2Port #(addr, data) )
   provisos(Bits#(addr, addr_sz),
            Bits#(data, data_sz),
            DefaultValue#(data) );

\end{libverbatim}
\\
\hline
\end{tabular}

\index{mkSyncBRAM2ServerBE@\te{mkSyncBRAM2ServerBE} (module)}
\index[function]{BRAM!mkSyncBRAM2ServerBE}

\begin{tabular}{|p{1.4 in}|p{4.2 in}|}
\hline
& \\
\te{mkSyncBRAM2ServerBE}&2 port, dual clock, byte-enabled BRAM Server
module. The 
\te{portA} subinterface and \te{portAClear} methods are in the
\te{clkA} domain;
the \te{portB} subinterface and \te{portBClear} methods are in the \te{clkB}
domain. \\
\cline{2-2}
& \begin{libverbatim}
(* no_default_clock, no_default_reset *)
module mkSyncBRAM2ServerBE #(BRAM_Configure cfg,
                             Clock clkA, Reset rstNA,
                             Clock clkB, Reset rstNB )
                          (BRAM2PortBE #(addr, data, n))
   provisos(Bits#(addr, addr_sz),
            Bits#(data, data_sz),
            Div#(data_sz, n, chunk_sz),
            Mul#(chunk_sz, n, data_sz) );
\end{libverbatim}
\\
\hline
\end{tabular}




{\bf Example: Using a BRAM}
\begin{verbatim}
import BRAM::*;
import StmtFSM::*;
import Clocks::*;

function BRAMRequest#(Bit#(8), Bit#(8)) makeRequest(Bool write, Bit#(8) addr, Bit#(8) data);
   return BRAMRequest{
                      write: write,
                      responseOnWrite:False,
                      address: addr,
                      datain: data
                      };
endfunction

(* synthesize *)
module sysBRAMTest();
    BRAM_Configure cfg = defaultValue;
    cfg.allowWriteResponseBypass = False;
    BRAM2Port#(Bit#(8), Bit#(8)) dut0 <- mkBRAM2Server(cfg);
    cfg.loadFormat = tagged Hex "bram2.txt";
    BRAM2Port#(Bit#(8), Bit#(8)) dut1 <- mkBRAM2Server(cfg);

   //Define StmtFSM to run tests
   Stmt test =
   (seq
       delay(10);
       ...
       action
          dut1.portA.request.put(makeRequest(False, 8'h02, 0));
          dut1.portB.request.put(makeRequest(False, 8'h03, 0));
       endaction
       action 
          $display("dut1read[0] = %x", dut1.portA.response.get);
          $display("dut1read[1] = %x", dut1.portB.response.get);
       endaction
       ...
       delay(100);
    endseq);
   mkAutoFSM(test);
endmodule
\end{verbatim}
