aboutsummaryrefslogblamecommitdiff
path: root/config/core_config.py
blob: 3f75243e8568c513c48c2b581ec69bf6764ac992 (plain) (tree)
1
2
3
4
5
6
7
8





                                                               
                                                                        
                                                            



























                                                                          
                                                                    
                                                               
                                                                 










                                                                     










                                                                                                   




                                                                                                                           






                                                                                    












                                                                             


                             











                                                                   



                                     
                                                   
 
                          
                               
 
                       
                          
                                                              










                                                                                
                            

                                                                     
                                                                       































                                                                                 










                                                                          












                                                                      





                                                                   

                                            
                        
                                         





                                                                


                          


                                    


                                                   


                                     

                                                                         
                                                        





















                                                                                                                        












                                                                  

                                                                                                                     









                                                                                                                          






                                                                                                                                    







                                                                                                                          

                                                                                                                                                                

                         


                                                                                                                   

                        


                                                                                                                               
 
















                                                                               







                                                                     
                                                                                                     








                                                                                                              
                                                                             
 
                                               

                    



                                                                                           









                                                                 
                                                                           

                                                                       
                                          


                                                                     


                                                                                                                                                                                                                   
 
                                               

                    



                                                                                           


                                                               
                                                                 





                      
                                    


                                                                     
            

   










                                                                          
                            





                                                     


                                    
                                             





                                 


                                                            


                                      
                  







                                                                     

                                                                                                      
                                                  
                                                                          















































                                                                        
#!/usr/bin/env python

"""
Generate core_selector.v and core_vfiles.mk for a set of cores.
"""

#=======================================================================
# Copyright (c) 2015-2017, NORDUnet A/S All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
# - Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
# - Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#
# - Neither the name of the NORDUnet nor the names of its contributors may
#   be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#=======================================================================

# The modexpa7 core drags in a one clock cycle delay to other cores,
# to compensate for the extra clock cycle consumed by the block
# memories used in the modexpa7 core.  We probably want a general
# solution for this, because we're going to run into this problem for
# any core that handles arguments big enough to require block memory.

# To Do:
#
# - Consider automating the one-clock-cycle delay stuff by adding
#   another boolean flag to the config file.  Default would be no
#   delay, if any included core sets the "I use block memories" flag,
#   all other cores would get the delay.  Slightly tedious but
#   something we can calculate easily enough, and probably an
#   improvement over wiring in the delay when nothing needs it.

def main():
    """
    Parse arguments and config file, generate core list, generate output.
    """

    from argparse import ArgumentParser, FileType, ArgumentDefaultsHelpFormatter
    from sys      import exit

    parser = ArgumentParser(description = __doc__, formatter_class = ArgumentDefaultsHelpFormatter)
    parser.add_argument("-d", "--debug",   help = "enable debugging",   action = "store_true")
    parser.add_argument("-c", "--config",  help = "configuration file", default = "core.cfg",       type = FileType("r"))
    parser.add_argument("-b", "--board",   help = "config file 'board' section")
    parser.add_argument("-p", "--project", help = "config file 'project' section")
    parser.add_argument("-v", "--verilog", help = "verilog output file",default = "core_selector.v",  type = FileType("w"))
    parser.add_argument("-m", "--makefile",help = "output makefile",    default = "core_vfiles.mk",   type = FileType("w"))
    parser.add_argument("core",            help = "name(s) of core(s)", nargs = "*")
    args = parser.parse_args()

    try:
        cfg = RawConfigParser()
        cfg.readfp(args.config)

        board = args.board or cfg.get("default", "board")
        board_section = "board " + board
        Core.bus_name = cfg.get(board_section, "bus name")
        Core.bus_width = int(cfg.get(board_section, "bus width"))
        Core.bus_max = Core.bus_width - 1
        Core.addr_width = Core.bus_width - 8
        Core.addr_max = Core.addr_width - 1
        Core.extra_wires = cfg.get(board_section, "extra wires")
        Core.modexp = cfg.get(board_section, "modexp")
        if Core.extra_wires:
            # restore formatting
            Core.extra_wires = Core.extra_wires.replace("\n", "\n   ") + "\n"

        if args.core:
            cores = args.core
        else:
            project = args.project or cfg.get("default", "project")
            cores = cfg.get("project " + project, "cores").split()

        for core in cfg.getvalues(board_section, "requires"):
            try:
                (c1, c2) = core.split("/")
                if c1 not in cores and c2 not in cores:
                    cores.append(c2)
            except ValueError:
                if core not in cores:
                    cores.append(core)
                

        cores.insert(0, "board_regs")
        cores.insert(1, "comm_regs")

        cores = tuple(Core(core) for core in cores)

        for core in cores:
            core.configure(cfg)

        core_number = 0
        for core in cores:
            core_number = core.assign_core_number(core_number)

        if False:

            # For some reason, attempting to optimize out the delay
            # code entirely results in a non-working bitstream.  Don't
            # know why, disabling the optimization works, so just do
            # that for now.

            Core.need_one_cycle_delay = any(core.block_memory for core in cores)

        args.verilog.write(createModule_template.format(
            core = cores[0],
            addrs = "".join(core.createAddr()     for core in cores),
            insts = "".join(core.createInstance() for core in cores),
            muxes = "".join(core.createMux()      for core in cores) ))

        args.makefile.write(listVfiles_template.format(
            vfiles = "".join(core.listVfiles()    for core in cores)))

    except Exception, e:
        if args.debug:
            raise
        exit(str(e))


try:
    import ConfigParser as configparser
except ImportError:
    import configparser   

class RawConfigParser(configparser.RawConfigParser):
    """
    RawConfigParser with a few extensions.
    """

    def getboolean(self, section, option, default = False):
        if self.has_option(section, option):
            # RawConfigParser is an old-stle class, super() doesn't work, feh.
            return configparser.RawConfigParser.getboolean(self, section, option)
        else:
            return default

    def getvalues(self, section, option):
        if self.has_option(section, option):
            for value in self.get(section, option).split():
                yield value

    def get(self, section, option):
        try:
            return configparser.RawConfigParser.get(self, section, option)
        except configparser.NoSectionError:
            if section in ("core board_regs", "core comm_regs"):
                return ""
            else:
                raise
        except configparser.NoOptionError:
            return ""


class Core(object):
    """
    Data and methods for a generic core.  We can use this directly for
    most cores, a few are weird and require subclassing to override
    particular methods.
    """

    # Class variable tracking how many times a particular core has
    # been instantiated.  This controls instance numbering.

    _instance_count = {}

    # Class variable recording whether we need a one-cycle delay to
    # compensate for block memories.

    need_one_cycle_delay = True

    def __init__(self, name):
        if Core.modexp and name == "modexp":
            name = Core.modexp
        self.name = name
        self.cfg_section = "core " + name
        self.core_number = None
        self.vfiles = []
        self.error_wire = True
        self.block_memory = False
        self.instance_number = self._instance_count.get(name, 0)
        self._instance_count[name] = self.instance_number + 1
        self.subcores = []
        self.blocks = 1
        self.dummy = False

    def assign_core_number(self, n):
        self.core_number = n
        for i, subcore in enumerate(self.subcores):
            subcore.assign_core_number(n + i + 1)
        return n + self.blocks

    def configure(self, cfg):
        if self.instance_number == 0:
            self.vfiles.extend(cfg.getvalues(self.cfg_section, "vfiles"))
            for required in cfg.getvalues(self.cfg_section, "requires"):
                if required not in self._instance_count:
                    self.vfiles.extend(cfg.getvalues("core " + required, "vfiles"))
        self.error_wire = cfg.getboolean(self.cfg_section, "error wire", self.error_wire)
        self.block_memory = cfg.getboolean(self.cfg_section, "block memory", self.block_memory)
        self.extra_ports = cfg.get(self.cfg_section, "extra ports")
        if self.extra_ports:
            self.extra_ports = self.extra_ports.replace("\n", "\n      ") + "\n"
        self.blocks = int(cfg.get(self.cfg_section, "core blocks") or 1)
        self.block_max = self.blocks - 1
        if self.blocks > 1:
            try:
                self.block_bits = {4:2, 8:3, 16:4}[self.blocks]
            except KeyError:
                raise ValueError, "In [{}]: unexpected value \"core blocks = {}\"".format(self.cfg_section, self.blocks)
            self.block_bit_max = self.block_bits - 1
        for subcore in cfg.getvalues(self.cfg_section, "subcores"):
            self.subcores.append(SubCore(subcore, self))
        if len(self.subcores) > self.blocks - 1:
            raise ValueError, "In [{}]: number of subcores exceeds size of \"core blocks\"".format(self.cfg_section)
        self.module_name = cfg.get(self.cfg_section, "module name") or self.name
        self.dummy = cfg.get(self.cfg_section, "dummy")
        if self.dummy:
            self.dummy = self.dummy.replace("\n", "\n   ") + "\n"

    @property
    def instance_name(self):
        if self._instance_count[self.name] > 1:
            return "{}_{}".format(self.name, self.instance_number)
        else:
            return self.name

    @property
    def upper_instance_name(self):
        return self.instance_name.upper()

    @property
    def error_wire_decl(self):
        return "\n   wire                 error_{core.instance_name};".format(core = self) if self.error_wire else ""

    @property
    def error_port(self):
        return ",\n      .error(error_{core.instance_name})".format(core = self) if self.error_wire else ""

    @property
    def one_cycle_delay(self):
        return one_cycle_delay_template.format(core = self) if self.need_one_cycle_delay and not self.block_memory else ""

    @property
    def mux_core_addr(self):
        if self.blocks == 1 or self.subcores:
            return "CORE_ADDR_{core.upper_instance_name}".format(core=self)
        else:
            return ",\n       ".join("CORE_ADDR_{core.upper_instance_name} + {0}".format(i, core=self) for i in range(self.blocks)) 

    @property
    def mux_data_reg(self):
        return "read_data_" + self.instance_name + ("_reg" if self.need_one_cycle_delay and not self.block_memory else "")

    @property
    def mux_error_reg(self):
        return "error_" + self.instance_name if self.error_wire else "0"

    def createInstance(self):
        template = createInstance_template_dummy if self.dummy else createInstance_template_generic if self.blocks == 1 else createInstance_template_multi_block
        return template.format(core = self)

    def createAddr(self):
        if self.dummy:
            return ""
        return createAddr_template.format(core = self) + "".join(subcore.createAddr() for subcore in self.subcores)

    def createMux(self):
        if self.dummy:
            return ""
        return createMux_template.format(core = self, core0 = self) + "".join(subcore.createMux() for subcore in self.subcores)

    def listVfiles(self):
        return "".join(" \\\n\t$(CORE_TREE)/" + vfile for vfile in self.vfiles)


class SubCore(Core):
    """"
    Override mux handling for TRNG's sub-cores.
    """

    def __init__(self, name, parent):
        super(SubCore, self).__init__(name)
        self.parent = parent

    def createMux(self):
        return createMux_template.format(core = self, core0 = self.parent)


# Templates (format strings), here instead of inline in the functions
# that use them, both because some of these are shared between
# multiple functions and because it's easier to read these (and get
# the indentation right) when the're separate.

# Template used by .createAddr() methods.

createAddr_template = """\
   localparam   CORE_ADDR_{core.upper_instance_name:21s} = {core.addr_width}'h{core.core_number:02x};
"""

# Template used by Core.createInstance().

createInstance_template_generic = """\
   //----------------------------------------------------------------
   // {core.upper_instance_name}
   //----------------------------------------------------------------
   wire                 enable_{core.instance_name} = (addr_core_num == CORE_ADDR_{core.upper_instance_name});
   wire [31: 0]         read_data_{core.instance_name};{core.error_wire_decl}

   {core.module_name} {core.instance_name}_inst
     (
      .clk(sys_clk),
      .reset_n(sys_rst_n),
{core.extra_ports}
      .cs(enable_{core.instance_name} & (sys_{core.bus_name}_rd | sys_{core.bus_name}_wr)),
      .we(sys_{core.bus_name}_wr),

      .address(addr_core_reg),
      .write_data(sys_write_data),
      .read_data(read_data_{core.instance_name}){core.error_port}
      );

{core.one_cycle_delay}

"""

# Template used for multi-block cores (modexp and trng).  This is different
# enough from the base template that it's easier to make this separate.

createInstance_template_multi_block = """\
   //----------------------------------------------------------------
   // {core.upper_instance_name}
   //----------------------------------------------------------------
   wire                 enable_{core.instance_name} = (addr_core_num >= CORE_ADDR_{core.upper_instance_name}) && (addr_core_num <= CORE_ADDR_{core.upper_instance_name} + {core.addr_width}'h{core.block_max:02x});
   wire [31: 0]         read_data_{core.instance_name};{core.error_wire_decl}
   wire [{core.block_bit_max}:0]           {core.instance_name}_prefix = addr_core_num[{core.block_bit_max}:0] - CORE_ADDR_{core.upper_instance_name};

   {core.module_name} {core.instance_name}_inst
     (
      .clk(sys_clk),
      .reset_n(sys_rst_n),
{core.extra_ports}
      .cs(enable_{core.instance_name} & (sys_{core.bus_name}_rd | sys_{core.bus_name}_wr)),
      .we(sys_{core.bus_name}_wr),

      .address({{{core.instance_name}_prefix, addr_core_reg}}),
      .write_data(sys_write_data),
      .read_data(read_data_{core.instance_name}){core.error_port}
      );

{core.one_cycle_delay}

"""

createInstance_template_dummy = """\
   //----------------------------------------------------------------
   // {core.upper_instance_name}
   //----------------------------------------------------------------
{core.dummy}
"""

# Template for one-cycle delay code.

one_cycle_delay_template = """\
   reg  [31: 0] read_data_{core.instance_name}_reg;
   always @(posedge sys_clk)
     read_data_{core.instance_name}_reg <= read_data_{core.instance_name};
"""

# Template for .createMux() methods.

createMux_template = """\
       {core.mux_core_addr}:
         begin
            sys_read_data_mux = {core0.mux_data_reg};
            sys_error_mux = {core0.mux_error_reg};
         end
"""

# Top-level (createModule) template.

createModule_template = """\
// NOTE: This file is generated; do not edit.

module core_selector
  (
   input wire          sys_clk,
   input wire          sys_rst_n,

   input wire [{core.bus_max}: 0]  sys_{core.bus_name}_addr,
   input wire          sys_{core.bus_name}_wr,
   input wire          sys_{core.bus_name}_rd,
   output wire [31: 0] sys_read_data,
   input wire [31: 0]  sys_write_data,
   output wire         sys_error,
{core.extra_wires}
   input wire          noise,
   output wire [7 : 0] debug
   );


   //----------------------------------------------------------------
   // Address Decoder
   //----------------------------------------------------------------
   // upper {core.addr_width} bits specify core being addressed
   wire [{core.addr_max:>2}: 0]         addr_core_num   = sys_{core.bus_name}_addr[{core.bus_max}: 8];
   // lower 8 bits specify register offset in core
   wire [ 7: 0]         addr_core_reg   = sys_{core.bus_name}_addr[ 7: 0];


   //----------------------------------------------------------------
   // Core Address Table
   //----------------------------------------------------------------
{addrs}

{insts}
   //----------------------------------------------------------------
   // Output (Read Data) Multiplexer
   //----------------------------------------------------------------
   reg [31: 0]          sys_read_data_mux;
   assign               sys_read_data = sys_read_data_mux;
   reg                  sys_error_mux;
   assign               sys_error = sys_error_mux;

   always @*

     case (addr_core_num)
{muxes}
       default:
         begin
            sys_read_data_mux = {{32{{1'b0}}}};
            sys_error_mux = 1;
         end
     endcase


endmodule


//======================================================================
// EOF core_selector.v
//======================================================================
"""

# Template for makefile snippet listing Verilog source files.

listVfiles_template = """\
# NOTE: This file is generated; do not edit by hand.

vfiles +={vfiles}
"""

# Run main program.

if __name__ == "__main__":
    main()