aboutsummaryrefslogblamecommitdiff
path: root/config/config.py
blob: 2a11bd6f2be3cff7148c8e2332a596a9e52fc065 (plain) (tree)
1
2
3
4
5
                     


                                            
 





















                                                                     
                                                       



                                      







                                                                      
 















                                                                      
 
           








                                                                                                   
                                                                                              
                                                                        


                                                                                                                             
                                                                                    

                              
        


                               


                             
                                                                           
                                                     
 

                                     
 
                                                       


                                                              

                                
 

                                                                     
                                                                     



                                                                      
 




                        

                   







                                                                      

                        


                                                                




                               
                        










                                                                


                                                              



                                                                               
 










                                                                  



                              
                             
                                                                  






                                                                   
                         


                                                                               
                              
       

                                                                        

       


                                   
 

                    


                                               








                                                                          
                                  





                                                                        
 

                                                                                      

                                            
                                                                                 






                                                       



                                                    

                                                               






                                                                                                              

                         




                                                                    











                                                                   
                                                                      
                                                                                
 
                          





                                    

                                    







                                                                     




                                                                                     

                                         



                                                                     
                                                                                                              





                                                       
                       








                                                                   


















                                                                                                                                                                             
                                                











                                                                   


   



                                                                      



                                                                     
                                                                                                                                                                                      

                                                       
                                                                                                                



                                        
                       



                                                                   
                                                               






                                                 



                                                                          


   

                                    


                                            
                                                                    



                                                        













                                                               

                                    





























































                                                                        
 


                                                             

                                                    


                 
                   


                          
#!/usr/bin/env python
"""
Generate core_selector.v for a set of cores.
"""

# History of cryptech bus addressing scheme, as best I understand it.
#
# The old old addressing scheme that Joachim and Paul came up with
# was:
#
#  3 bits of segment  selector [16:14]
#  6 bits of core     selector [13:8]
#  8 bits of register selector [7:0]
#
# modexp6s needed more register bits than that, so Pavel changed
# addressing within the math segment to:
#
#  3 bits of segment  selector [16:14]
#  4 bits of core     selector [13:10]
# 10 bits of register selector [9:0]
#
# Meanwhile, Paul eliminated segments entirely when writing the
# ancestor of this script, resulting in:
#
#  9 bits of core     selector [16:8]
#  8 bits of register selector [7:0]
#
# Taking Pavel's and Paul's changes together, we'd get:
#
#  7 bits of core     selector [16:10]
# 10 bits of register selector [9:0]
#
# Except that this would waste space for most cores, and make things
# very confusing for the TRNG cores.  So, instead, we keep Paul's
# two-level (no segment) scheme and handle modexps6 as a set of four
# consecutive "cores" with a 10-bit composite register selector.

# The modexps6 core also 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 modexps6 core.

# To Do:
#
# - Move reset-high/reset-low to a boolean variable in the config
#   file, simplify Core classes accordingly.
#
# - 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.
#
# - Rename script and its config file to something more meaningful.
#
# - Figure out whether this really belongs in the novena repository at
#   all, seems more generic than that.

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

    from argparse       import ArgumentParser, FileType, ArgumentDefaultsHelpFormatter
    from ConfigParser   import RawConfigParser
    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("-s", "--section", help = "config file section")
    parser.add_argument("-c", "--config",  help = "configuration file",   default = "config.cfg",       type = FileType("r"))
    parser.add_argument("--verilog",       help = "verilog output file",  default = "core_selector.v",  type = FileType("w"))
    parser.add_argument("--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)

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

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

        cores = tuple(Core.new(core) for core in cores)
        core_number = 0
        for core in cores:
            core_number = core.assign_core_number(core_number)
        for core in cores[2:]:
            core.add_vfiles(cfg)

        args.verilog.write(createModule_template.format(
            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))


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 = {}

    # Map from core name to subclass for the special case cores.

    special_class = {}

    def __init__(self, name):
        self.name = name
        self.core_number = None
        self.vfiles = ()
        self.instance_number = self._instance_count.get(name, 0)
        self._instance_count[name] = self.instance_number + 1

    @classmethod
    def new(cls, name):
        return cls.special_class.get(name, cls)(name)

    def assign_core_number(self, n):
        self.core_number = n
        return n + 1

    def add_vfiles(self, cfg):
        if self.instance_number == 0:
            self.vfiles = cfg.get(self.name, "vfiles").split()
            if cfg.has_option(self.name, "requires"):
                for required in cfg.get(self.name, "requires").split():
                    if required not in self._instance_count:
                        self.vfiles.extend(cfg.get(required, "vfiles").split())

    @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 reset_pin(self):
        return ".rst(sys_rst)"

    def createInstance(self):
        return createInstance_template_generic.format(core = self)

    def createAddr(self):
        return createAddr_template.format(core = self)

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

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


class InvertedResetCore(Core):
    """
    Core which inverts the reset signal.  Seems to vary by author.
    No, I don't know why we don't just pick one convention or the other.
    """

    @property
    def reset_pin(self):
        return ".reset_n(~sys_rst)"


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)


class TRNGCore(InvertedResetCore):
    """
    The TRNG core has an internal mux and a collection of sub-cores.
    Mostly this means that our method calls have to iterate over all
    of the subcores after handling the base TRNG core, but we also use
    a different instance template in the hope that it is easier to read.
    """

    subcore_names = ("avalanche_entropy", "rosc_entropy", "trng_mixer", "trng_csprng")

    def __init__(self, name):
        super(TRNGCore, self).__init__(name)
        self.subcores = tuple(SubCore(name, self) for name in self.subcore_names)

    def assign_core_number(self, n):
        n = super(TRNGCore, self).assign_core_number(n)
        for subcore in self.subcores:
            n = subcore.assign_core_number(n)
        return n

    @property
    def last_subcore_upper_instance_name(self):
        return self.subcores[-1].upper_instance_name

    def createInstance(self):
        return createInstance_template_TRNG.format(core = self)

    def createAddr(self):
        return super(TRNGCore, self).createAddr() + "".join(subcore.createAddr() for subcore in self.subcores)

    def createMux(self):
        return super(TRNGCore, self).createMux() + "".join(subcore.createMux() for subcore in self.subcores)


class ModExpS6Core(Core):
    """
    ModExpS6 core consumes as much space as four ordinary cores, and
    uses different templates to handle the differences in timing and
    addressing.
    """

    def assign_core_number(self, n):
        n = super(ModExpS6Core, self).assign_core_number(n)
        return n + 3

    def createInstance(self):
        return createInstance_template_ModExpS6.format(core = self)

    def createMux(self):
        return createMux_modexps6_template.format(core = self)


# Hook special classes in as handlers for the cores that require them.
# Moving the reset-high/reset-low logic to the config file should simplify this.

Core.special_class.update(
    trng        = TRNGCore,
    aes         = InvertedResetCore,
    chacha      = InvertedResetCore,
    sha1        = InvertedResetCore,
    sha256      = InvertedResetCore,
    sha512      = InvertedResetCore,
    modexp      = InvertedResetCore,
    modexps6    = ModExpS6Core)


# 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} = 9'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};
   wire                 error_{core.instance_name};

   {core.name} {core.instance_name}_inst
     (
      .clk(sys_clk),
      {core.reset_pin},

      .cs(enable_{core.instance_name} & (sys_eim_rd | sys_eim_wr)),
      .we(sys_eim_wr),

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

   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 used by ModExpS6Core.createInstance().  This is different
# enough from the base template that it's easier to make this separate.

createInstance_template_ModExpS6 = """\
   //----------------------------------------------------------------
   // {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} + 3);
   wire [31: 0]         read_data_{core.instance_name};
   wire                 error_{core.instance_name};
   wire [1:0]           {core.instance_name}_prefix = addr_core_num[1:0] - CORE_ADDR_{core.upper_instance_name};

   {core.name}_wrapper {core.instance_name}_inst
     (
      .clk(sys_clk),
      {core.reset_pin},

      .cs(enable_{core.instance_name} & (sys_eim_rd | sys_eim_wr)),
      .we(sys_eim_wr),

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


"""

# Template used by TRNGCore.createInstance(); this is different enough
# from the generic template that it's (probably) clearer to have this
# separate.

createInstance_template_TRNG = """\
   //----------------------------------------------------------------
   // {core.upper_instance_name}
   //----------------------------------------------------------------
   wire                 enable_{core.instance_name} = (addr_core_num >= CORE_ADDR_{core.upper_instance_name}) && (addr_core_num <= CORE_ADDR_{core.last_subcore_upper_instance_name});
   wire [31: 0]         read_data_{core.instance_name};
   wire                 error_{core.instance_name};
   wire [3:0]           {core.instance_name}_prefix = addr_core_num[3:0] - CORE_ADDR_{core.upper_instance_name};

   {core.name} {core.instance_name}_inst
     (
      .clk(sys_clk),
      {core.reset_pin},

      .cs(enable_{core.instance_name} & (sys_eim_rd | sys_eim_wr)),
      .we(sys_eim_wr),

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

      .avalanche_noise(noise),
      .debug(debug)
      );

   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_ADDR_{core.upper_instance_name}:
         begin
            sys_read_data_mux = read_data_{core0.instance_name}_reg;
            sys_error_mux = error_{core0.instance_name};
         end
"""

# Template for ModExpS6.createMux() method.

createMux_modexps6_template = """\
       CORE_ADDR_{core.upper_instance_name} + 0,
       CORE_ADDR_{core.upper_instance_name} + 1,
       CORE_ADDR_{core.upper_instance_name} + 2,
       CORE_ADDR_{core.upper_instance_name} + 3:
         begin
            sys_read_data_mux = read_data_{core.instance_name};
            sys_error_mux = error_{core.instance_name};
         end
"""


# Top-level (createModule) template.

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

module core_selector
  (
   input wire          sys_clk,
   input wire          sys_rst,

   input wire [16: 0]  sys_eim_addr,
   input wire          sys_eim_wr,
   input wire          sys_eim_rd,
   output wire [31: 0] sys_read_data,
   input wire [31: 0]  sys_write_data,
   output wire         sys_error,

   input wire          noise,
   output wire [7 : 0] debug
   );


   //----------------------------------------------------------------
   // Address Decoder
   //----------------------------------------------------------------
   // upper 9 bits specify core being addressed
   wire [ 8: 0]         addr_core_num   = sys_eim_addr[16: 8];
   // lower 8 bits specify register offset in core
   wire [ 7: 0]         addr_core_reg   = sys_eim_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()