diff --git a/assembler_tools/hec-assembler-tools/README.md b/assembler_tools/hec-assembler-tools/README.md
index a410b92f..ec3456dd 100644
--- a/assembler_tools/hec-assembler-tools/README.md
+++ b/assembler_tools/hec-assembler-tools/README.md
@@ -9,6 +9,7 @@ The tools in this directory are the reference implementation of and Assembler co
1. [Assembler Instruction Specs](#asm_specs)
4. [Executing the Assembler](#executing_asm)
1. [Running for a Pre-Generated Kernel](#executing_single)
+ 2. [Hardware Spec Configuration](#spec_configuration)
5. [Debug Tools](./debug_tools/README.md)
## Dependencies
@@ -98,3 +99,21 @@ python3 he_prep.py -h
python3 he_as.py -h
python3 he_link.py -h
```
+
+### Hardware Spec Configuration
+
+#### Default Configuration
+The HERACLES assembler comes with default configurations for ISA and Memory parameters. These configurations are defined in JSON files located in the config folder:
+- **ISA Specification:** config/isa_spec.json
+- **Memory Specification:** config/mem_spec.json
+
+These files contain default values for various parameters that affect the behavior of the assembler and linker. Users can modify these files directly, but it is recommended to create a copy and use the command-line flags to specify the path to the modified configuration files. This approach ensures that the original default files remain unchanged and can be used as a reference.
+
+#### Modifying Configuration
+To modify the ISA or Memory specifications, follow these steps:
+- Create a Copy: Copy the default JSON files from the config folder to a new location.
+- Edit the Copy: Modify the parameters in the copied JSON files as needed.
+- **Specify the Path:** Use the **--isa_spec** and **--mem_spec** command-line flags to specify the path to your modified configuration files when running the scripts.
+
+#### Additional Notes
+Memory Parameters: For he_as.py and he_link.py, certain memory parameters like spad_size and hbm_size can be overwritten directly from the command line, providing flexibility in configuration without modifying the JSON files.
\ No newline at end of file
diff --git a/assembler_tools/hec-assembler-tools/assembler/common/constants.py b/assembler_tools/hec-assembler-tools/assembler/common/constants.py
index 9c11090c..999f84f3 100644
--- a/assembler_tools/hec-assembler-tools/assembler/common/constants.py
+++ b/assembler_tools/hec-assembler-tools/assembler/common/constants.py
@@ -23,6 +23,9 @@ class Constants:
OPERATIONS (list): List of high-level operations supported by the system.
"""
+ __MAX_BUNDLE_SIZE: int
+ __XINSTRUCTION_SIZE_BYTES: int
+
# Data Constants
# --------------
@@ -70,12 +73,12 @@ def REPLACEMENT_POLICIES(cls) -> tuple:
@classproperty
def XINSTRUCTION_SIZE_BYTES(cls) -> int:
"""Size of an x-instruction in bytes."""
- return 8
+ return cls.__XINSTRUCTION_SIZE_BYTES
@classproperty
def MAX_BUNDLE_SIZE(cls) -> int:
"""Maximum number of instructions in a bundle."""
- return 64
+ return cls.__MAX_BUNDLE_SIZE
@classproperty
def MAX_BUNDLE_SIZE_BYTES(cls) -> int:
@@ -98,6 +101,25 @@ def OPERATIONS(cls) -> list:
"square", "add_plain", "add_corrected", "mul_plain", "rescale",
"boot_dot_prod", "boot_mod_drop_scale", "boot_mul_const", "boot_galois_plain" ]
+ @classmethod
+ def hw_spec_as_dict(cls) -> dict:
+ """
+ Returns hw configurable attributes as dictionary.
+ """
+ dict = {"bytes_per_xinstruction": cls.XINSTRUCTION_SIZE_BYTES,
+ "max_instructions_per_bundle": cls.MAX_BUNDLE_SIZE}
+ return dict
+
+ @classmethod
+ def setMaxBundleSize(cls, val: int):
+ """Updates max bundle size"""
+ cls.__MAX_BUNDLE_SIZE = val
+
+ @classmethod
+ def setXInstructionSizeBytes(cls, val: int):
+ """Updates size of single XInstruction"""
+ cls.__XINSTRUCTION_SIZE_BYTES = val
+
def convertBytes2Words(bytes: int) -> int:
"""
Converts a size in bytes to the equivalent number of words.
@@ -290,14 +312,21 @@ class MemoryModel:
HBM and SPAD.
"""
- __XINST_QUEUE_MAX_CAPACITY = 1 * Constants.MEGABYTE
- __XINST_QUEUE_MAX_CAPACITY_WORDS = convertBytes2Words(__XINST_QUEUE_MAX_CAPACITY)
- __CINST_QUEUE_MAX_CAPACITY = 128 * Constants.KILOBYTE
- __CINST_QUEUE_MAX_CAPACITY_WORDS = convertBytes2Words(__CINST_QUEUE_MAX_CAPACITY)
- __MINST_QUEUE_MAX_CAPACITY = 128 * Constants.KILOBYTE
- __MINST_QUEUE_MAX_CAPACITY_WORDS = convertBytes2Words(__MINST_QUEUE_MAX_CAPACITY)
- __STORE_BUFFER_MAX_CAPACITY = 128 * Constants.KILOBYTE
- __STORE_BUFFER_MAX_CAPACITY_WORDS = convertBytes2Words(__STORE_BUFFER_MAX_CAPACITY)
+ __XINST_QUEUE_MAX_CAPACITY: int
+ __CINST_QUEUE_MAX_CAPACITY: int
+ __MINST_QUEUE_MAX_CAPACITY: int
+ __STORE_BUFFER_MAX_CAPACITY: int
+
+ # Class-level attributes
+ __NUM_BLOCKS_PER_TWID_META_WORD: int
+ __NUM_BLOCKS_PER_KGSEED_META_WORD: int
+ __NUM_ROUTING_TABLE_REGISTERS: int
+ __NUM_ONES_META_REGISTERS: int
+ __NUM_TWIDDLE_META_REGISTERS: int
+ __TWIDDLE_META_REGISTER_SIZE_BYTES: int
+ __MAX_RESIDUALS: int
+ __NUM_REGISTER_BANKS: int
+ __NUM_REGISTERS_PER_BANK: int
@classproperty
def XINST_QUEUE_MAX_CAPACITY(cls):
@@ -306,7 +335,7 @@ def XINST_QUEUE_MAX_CAPACITY(cls):
@classproperty
def XINST_QUEUE_MAX_CAPACITY_WORDS(cls):
"""Maximum capacity of the XINST queue in words."""
- return cls.__XINST_QUEUE_MAX_CAPACITY_WORDS
+ return convertBytes2Words(cls.__XINST_QUEUE_MAX_CAPACITY)
@classproperty
def CINST_QUEUE_MAX_CAPACITY(cls):
"""Maximum capacity of the CINST queue in bytes."""
@@ -314,7 +343,7 @@ def CINST_QUEUE_MAX_CAPACITY(cls):
@classproperty
def CINST_QUEUE_MAX_CAPACITY_WORDS(cls):
"""Maximum capacity of the CINST queue in words."""
- return cls.__CINST_QUEUE_MAX_CAPACITY_WORDS
+ return convertBytes2Words(cls.__CINST_QUEUE_MAX_CAPACITY)
@classproperty
def MINST_QUEUE_MAX_CAPACITY(cls):
"""Maximum capacity of the MINST queue in bytes."""
@@ -322,7 +351,7 @@ def MINST_QUEUE_MAX_CAPACITY(cls):
@classproperty
def MINST_QUEUE_MAX_CAPACITY_WORDS(cls):
"""Maximum capacity of the MINST queue in words."""
- return cls.__MINST_QUEUE_MAX_CAPACITY_WORDS
+ return convertBytes2Words(cls.__MINST_QUEUE_MAX_CAPACITY)
@classproperty
def STORE_BUFFER_MAX_CAPACITY(cls):
"""Maximum capacity of the store buffer in bytes."""
@@ -330,17 +359,17 @@ def STORE_BUFFER_MAX_CAPACITY(cls):
@classproperty
def STORE_BUFFER_MAX_CAPACITY_WORDS(cls):
"""Maximum capacity of the store buffer in words."""
- return cls.__STORE_BUFFER_MAX_CAPACITY_WORDS
+ return convertBytes2Words(cls.__STORE_BUFFER_MAX_CAPACITY)
@classproperty
def NUM_BLOCKS_PER_TWID_META_WORD(cls) -> int:
"""Number of blocks per twiddle metadata word."""
- return 4
+ return cls.__NUM_BLOCKS_PER_TWID_META_WORD
@classproperty
def NUM_BLOCKS_PER_KGSEED_META_WORD(cls) -> int:
"""Number of blocks per key generation seed metadata word."""
- return 4
+ return cls.__NUM_BLOCKS_PER_KGSEED_META_WORD
@classproperty
def NUM_ROUTING_TABLE_REGISTERS(cls) -> int:
@@ -351,7 +380,7 @@ def NUM_ROUTING_TABLE_REGISTERS(cls) -> int:
at the same time, since rshuffle instructions will pick a routing table
to use to compute the shuffled result.
"""
- return 1
+ return cls.__NUM_ROUTING_TABLE_REGISTERS
@classproperty
def NUM_ONES_META_REGISTERS(cls) -> int:
@@ -361,7 +390,7 @@ def NUM_ONES_META_REGISTERS(cls) -> int:
This directly affects the maximum number of residuals that can be
processed in the CE without needing to load new metadata.
"""
- return 1
+ return cls.__NUM_ONES_META_REGISTERS
@classproperty
def NUM_TWIDDLE_META_REGISTERS(cls) -> int:
@@ -371,14 +400,14 @@ def NUM_TWIDDLE_META_REGISTERS(cls) -> int:
This directly affects the maximum number of residuals that can be
processed in the CE without needing to load new metadata.
"""
- return 32 * cls.NUM_ONES_META_REGISTERS
+ return cls.__NUM_TWIDDLE_META_REGISTERS
@classproperty
def TWIDDLE_META_REGISTER_SIZE_BYTES(cls) -> int:
"""
Size, in bytes, of a twiddle factor metadata register.
"""
- return 8 * Constants.KILOBYTE
+ return cls.__TWIDDLE_META_REGISTER_SIZE_BYTES
@classproperty
def MAX_RESIDUALS(cls) -> int:
@@ -386,17 +415,128 @@ def MAX_RESIDUALS(cls) -> int:
Maximum number of residuals that can be processed in the CE without
needing to load new metadata.
"""
- return cls.NUM_TWIDDLE_META_REGISTERS * 2
+ return cls.__MAX_RESIDUALS
@classproperty
def NUM_REGISTER_BANKS(cls) -> int:
"""Number of register banks in the CE"""
- return 4
+ return cls.__NUM_REGISTER_BANKS
@classproperty
- def NUM_REGISTER_PER_BANKS(cls) -> int:
+ def NUM_REGISTERS_PER_BANK(cls) -> int:
"""Number of register per register banks in the CE"""
- return 72
+ return cls.__NUM_REGISTERS_PER_BANK
+
+ @classmethod
+ def hw_spec_as_dict(cls) -> dict:
+ """
+ Returns hw configurable attributes as dictionary.
+ """
+ dict = {"max_xinst_queue_size_in_bytes": cls.__XINST_QUEUE_MAX_CAPACITY,
+ "max_cinst_queue_size_in_bytes": cls.__CINST_QUEUE_MAX_CAPACITY,
+ "max_minst_queue_size_in_bytes": cls.__MINST_QUEUE_MAX_CAPACITY,
+ "max_store_buffer_size_in_bytes": cls.__STORE_BUFFER_MAX_CAPACITY,
+ "num_blocks_per_twid_meta_word": cls.NUM_BLOCKS_PER_TWID_META_WORD,
+ "num_blocks_per_kgseed_meta_word": cls.NUM_BLOCKS_PER_KGSEED_META_WORD,
+ "num_routing_table_registers": cls.NUM_ROUTING_TABLE_REGISTERS,
+ "num_ones_meta_registers": cls.NUM_ONES_META_REGISTERS,
+ "num_twiddle_meta_registers": cls.NUM_TWIDDLE_META_REGISTERS,
+ "twiddle_meta_register_size_in_bytes": cls.TWIDDLE_META_REGISTER_SIZE_BYTES,
+ "max_residuals": cls.MAX_RESIDUALS,
+ "num_register_banks": cls.NUM_REGISTER_BANKS,
+ "num_registers_per_bank": cls.NUM_REGISTERS_PER_BANK}
+ return dict
+
+ @classmethod
+ def setMaxXInstQueueCapacity(cls, val: int):
+ """
+ Sets max XInst queue capacity
+ """
+ cls.__XINST_QUEUE_MAX_CAPACITY = val
+
+ @classmethod
+ def setMaxCInstQueueCapacity(cls, val: int):
+ """
+ Sets max CInst queue capacity
+ """
+ cls.__CINST_QUEUE_MAX_CAPACITY = val
+
+ @classmethod
+ def setMaxMInstQueueCapacity(cls, val: int):
+ """
+ Sets max MInst queue capacity
+ """
+ cls.__MINST_QUEUE_MAX_CAPACITY = val
+
+ @classmethod
+ def setMaxStoreBufferCapacity(cls, val: int):
+ """
+ Sets max store buffer capacity
+ """
+ cls.__STORE_BUFFER_MAX_CAPACITY = val
+
+ @classmethod
+ def setNumBlocksPerTwidMetaWord(cls, val: int):
+ """
+ Sets the number of blocks per twiddle metadata word.
+ """
+ cls.__NUM_BLOCKS_PER_TWID_META_WORD = val
+
+ @classmethod
+ def setNumBlocksPerKgseedMetaWord(cls, val: int):
+ """
+ Sets the number of blocks per key generation seed metadata word.
+ """
+ cls.__NUM_BLOCKS_PER_KGSEED_META_WORD = val
+
+ @classmethod
+ def setNumRoutingTableRegisters(cls, val: int):
+ """
+ Sets the number of routing table registers.
+ """
+ cls.__NUM_ROUTING_TABLE_REGISTERS = val
+
+ @classmethod
+ def setNumOnesMetaRegisters(cls, val: int):
+ """
+ Sets the number of ones metadata registers.
+ """
+ cls.__NUM_ONES_META_REGISTERS = val
+
+ @classmethod
+ def setNumTwiddleMetaRegisters(cls, val: int):
+ """
+ Sets the number of twiddle metadata registers.
+ """
+ cls.__NUM_TWIDDLE_META_REGISTERS = val
+
+ @classmethod
+ def setTwiddleMetaRegisterSizeBytes(cls, val: int):
+ """
+ Sets the size of twiddle metadata register in bytes.
+ """
+ cls.__TWIDDLE_META_REGISTER_SIZE_BYTES = val
+
+ @classmethod
+ def setMaxResiduals(cls, val: int):
+ """
+ Sets the maximum number of residuals.
+ """
+ cls.__MAX_RESIDUALS = val
+
+ @classmethod
+ def setNumRegisterBanks(cls, val: int):
+ """
+ Sets the number of register banks.
+ """
+ cls.__NUM_REGISTER_BANKS = val
+
+ @classmethod
+ def setNumRegistersPerBank(cls, val: int):
+ """
+ Sets the number of registers per bank.
+ """
+ cls.__NUM_REGISTERS_PER_BANK = val
class HBM:
"""
@@ -404,8 +544,7 @@ class HBM:
This class defines the maximum capacity of HBM in both bytes and words.
"""
- __MAX_CAPACITY = 64 * Constants.GIGABYTE
- __MAX_CAPACITY_WORDS = convertBytes2Words(__MAX_CAPACITY)
+ __MAX_CAPACITY: int
@classproperty
def MAX_CAPACITY(cls) -> int:
@@ -415,7 +554,22 @@ def MAX_CAPACITY(cls) -> int:
@classproperty
def MAX_CAPACITY_WORDS(cls) -> int:
"""Total capacity of HBM in Words"""
- return cls.__MAX_CAPACITY_WORDS
+ return convertBytes2Words(cls.__MAX_CAPACITY)
+
+ @classmethod
+ def hw_spec_as_dict(cls) -> dict:
+ """
+ Returns hw configurable attributes as dictionary.
+ """
+ dict = {"max_hbm_size_in_bytes": cls.__MAX_CAPACITY}
+ return dict
+
+ @classmethod
+ def setMaxCapacity(cls, val: int):
+ """
+ Sets max SPAD Capacity
+ """
+ cls.__MAX_CAPACITY = val
class SPAD:
"""
@@ -423,8 +577,7 @@ class SPAD:
This class defines the maximum capacity of SPAD in both bytes and words.
"""
- __MAX_CAPACITY = 64 * Constants.MEGABYTE
- __MAX_CAPACITY_WORDS = convertBytes2Words(__MAX_CAPACITY)
+ __MAX_CAPACITY: int
# Class methods and properties
# ----------------------------
@@ -437,4 +590,19 @@ def MAX_CAPACITY(cls) -> int:
@classproperty
def MAX_CAPACITY_WORDS(cls) -> int:
"""Total capacity of SPAD in Words"""
- return cls.__MAX_CAPACITY_WORDS
+ return convertBytes2Words(cls.__MAX_CAPACITY)
+
+ @classmethod
+ def hw_spec_as_dict(cls) -> dict:
+ """
+ Returns hw configurable attributes as dictionary.
+ """
+ dict = {"max_cache_size_in_bytes": cls.__MAX_CAPACITY}
+ return dict
+
+ @classmethod
+ def setMaxCapacity(cls, val: int):
+ """
+ Sets max SPAD Capacity
+ """
+ cls.__MAX_CAPACITY = val
diff --git a/assembler_tools/hec-assembler-tools/assembler/common/run_config.py b/assembler_tools/hec-assembler-tools/assembler/common/run_config.py
index 83e9d06e..9737a962 100644
--- a/assembler_tools/hec-assembler-tools/assembler/common/run_config.py
+++ b/assembler_tools/hec-assembler-tools/assembler/common/run_config.py
@@ -1,25 +1,9 @@
import io
+from .decorators import *
from . import constants
from .config import GlobalConfig
-def static_initializer(cls):
- """
- Decorator to initialize static members of a class.
-
- This decorator calls the `init_static` method of the class to initialize
- any static members or configurations.
-
- Args:
- cls: The class to be initialized.
-
- Returns:
- The class with initialized static members.
- """
- cls.init_static()
- return cls
-
-@static_initializer
class RunConfig:
"""
Configuration class for running the assembler with specific settings.
@@ -31,11 +15,6 @@ class RunConfig:
__initialized = False # Specifies whether static members have been initialized
__default_config = {} # Dictionary of all configuration items supported and their default values
- # Config defaults
- DEFAULT_HBM_SIZE_KB = int(constants.MemoryModel.HBM.MAX_CAPACITY / constants.Constants.KILOBYTE)
- DEFAULT_SPAD_SIZE_KB = int(constants.MemoryModel.SPAD.MAX_CAPACITY / constants.Constants.KILOBYTE)
- DEFAULT_REPL_POLICY = constants.Constants.REPLACEMENT_POLICY_FTBU
-
def __init__(self,
**kwargs):
"""
@@ -81,6 +60,17 @@ def __init__(self,
if self.repl_policy not in constants.Constants.REPLACEMENT_POLICIES:
raise ValueError('Invalid `repl_policy`. "{}" not in {}'.format(self.repl_policy,
constants.Constants.REPLACEMENT_POLICIES))
+ @classproperty
+ def DEFAULT_HBM_SIZE_KB(cls) -> int:
+ return int(constants.MemoryModel.HBM.MAX_CAPACITY / constants.Constants.KILOBYTE)
+
+ @classproperty
+ def DEFAULT_SPAD_SIZE_KB(cls) -> int:
+ return int(constants.MemoryModel.SPAD.MAX_CAPACITY / constants.Constants.KILOBYTE)
+
+ @classproperty
+ def DEFAULT_REPL_POLICY(cls) -> int:
+ return constants.Constants.REPLACEMENT_POLICY_FTBU
@classmethod
def init_static(cls):
diff --git a/assembler_tools/hec-assembler-tools/assembler/memory_model/__init__.py b/assembler_tools/hec-assembler-tools/assembler/memory_model/__init__.py
index 2dfbb96b..7bf6ccfa 100644
--- a/assembler_tools/hec-assembler-tools/assembler/memory_model/__init__.py
+++ b/assembler_tools/hec-assembler-tools/assembler/memory_model/__init__.py
@@ -31,10 +31,6 @@ class StoreBufferValueType(NamedTuple):
variable: Variable
dest_spad_address: int
- __MAX_TWIDDLE_META_VARS_PER_SEGMENT = math.ceil(constants.MemoryModel.NUM_TWIDDLE_META_REGISTERS * \
- constants.MemoryModel.TWIDDLE_META_REGISTER_SIZE_BYTES / \
- constants.Constants.WORD_SIZE)
-
@classproperty
def MAX_TWIDDLE_META_VARS_PER_SEGMENT(cls):
"""
@@ -43,8 +39,9 @@ def MAX_TWIDDLE_META_VARS_PER_SEGMENT(cls):
Returns:
int: The number of variables per segment.
"""
- return cls.__MAX_TWIDDLE_META_VARS_PER_SEGMENT
-
+ return math.ceil(constants.MemoryModel.NUM_TWIDDLE_META_REGISTERS * \
+ constants.MemoryModel.TWIDDLE_META_REGISTER_SIZE_BYTES / \
+ constants.Constants.WORD_SIZE)
# Constructor
# -----------
@@ -52,7 +49,7 @@ def MAX_TWIDDLE_META_VARS_PER_SEGMENT(cls):
def __init__(self,
hbm_capacity_words: int,
spad_capacity_words: int,
- num_register_banks: int = constants.MemoryModel.NUM_REGISTER_BANKS,
+ num_register_banks: int,
register_range: range = None):
"""
Initializes a new MemoryModel object.
@@ -74,7 +71,7 @@ def __init__(self,
raise ValueError(('`num_register_banks`: there must be at least {} register banks, '
'but {} requested.').format(constants.MemoryModel.NUM_REGISTER_BANKS,
num_register_banks))
- self.__register_range = range(constants.MemoryModel.NUM_REGISTER_PER_BANKS) if not register_range else register_range
+ self.__register_range = range(constants.MemoryModel.NUM_REGISTERS_PER_BANK) if not register_range else register_range
# initialize members
self.__store_buffer = QueueDict() # QueueDict(var_name: str, StoreBufferValueType)
self.__variables = {} # dict(var_name, Variable)
diff --git a/assembler_tools/hec-assembler-tools/assembler/memory_model/register_file.py b/assembler_tools/hec-assembler-tools/assembler/memory_model/register_file.py
index 9925be9f..51868e8d 100644
--- a/assembler_tools/hec-assembler-tools/assembler/memory_model/register_file.py
+++ b/assembler_tools/hec-assembler-tools/assembler/memory_model/register_file.py
@@ -231,8 +231,8 @@ def __init__(self,
Raises:
ValueError: If the register index is out of the valid range.
"""
- if register_index < 0 or register_index >= constants.MemoryModel.NUM_REGISTER_PER_BANKS:
- raise ValueError((f'`register_index`: expected an index for register in the range [0, {constants.MemoryModel.NUM_REGISTER_PER_BANKS}), '
+ if register_index < 0 or register_index >= constants.MemoryModel.NUM_REGISTERS_PER_BANK:
+ raise ValueError((f'`register_index`: expected an index for register in the range [0, {constants.MemoryModel.NUM_REGISTERS_PER_BANK}), '
f'but {register_index} received.'))
super().__init__((0, 0))
self.register_dirty = False
diff --git a/assembler_tools/hec-assembler-tools/assembler/isa_spec/__init__.py b/assembler_tools/hec-assembler-tools/assembler/spec_config/isa_spec.py
similarity index 99%
rename from assembler_tools/hec-assembler-tools/assembler/isa_spec/__init__.py
rename to assembler_tools/hec-assembler-tools/assembler/spec_config/isa_spec.py
index eff3f06a..4476dd86 100644
--- a/assembler_tools/hec-assembler-tools/assembler/isa_spec/__init__.py
+++ b/assembler_tools/hec-assembler-tools/assembler/spec_config/isa_spec.py
@@ -4,7 +4,7 @@
import assembler.instructions.minst as minst
import assembler.instructions.xinst as xinst
-class SpecConfig:
+class ISASpecConfig:
__target_cops = {
"bload" : cinst.bload.Instruction,
"bones" : cinst.bones.Instruction,
diff --git a/assembler_tools/hec-assembler-tools/assembler/spec_config/mem_spec.py b/assembler_tools/hec-assembler-tools/assembler/spec_config/mem_spec.py
new file mode 100644
index 00000000..0fb5a061
--- /dev/null
+++ b/assembler_tools/hec-assembler-tools/assembler/spec_config/mem_spec.py
@@ -0,0 +1,122 @@
+import os
+import re
+import json
+from assembler.common.constants import Constants, MemoryModel
+
+class MemSpecConfig:
+
+ _target_attributes = {
+ "bytes_per_xinstruction": Constants.setXInstructionSizeBytes,
+ "max_instructions_per_bundle": Constants.setMaxBundleSize,
+ "max_xinst_queue_size_in_bytes": MemoryModel.setMaxXInstQueueCapacity,
+ "max_cinst_queue_size_in_bytes": MemoryModel.setMaxCInstQueueCapacity,
+ "max_minst_queue_size_in_bytes": MemoryModel.setMaxMInstQueueCapacity,
+ "max_store_buffer_size_in_bytes": MemoryModel.setMaxStoreBufferCapacity,
+ "num_blocks_per_twid_meta_word": MemoryModel.setNumBlocksPerTwidMetaWord,
+ "num_blocks_per_kgseed_meta_word": MemoryModel.setNumBlocksPerKgseedMetaWord,
+ "num_routing_table_registers": MemoryModel.setNumRoutingTableRegisters,
+ "num_ones_meta_registers": MemoryModel.setNumOnesMetaRegisters,
+ "num_twiddle_meta_registers": MemoryModel.setNumTwiddleMetaRegisters,
+ "twiddle_meta_register_size_in_bytes": MemoryModel.setTwiddleMetaRegisterSizeBytes,
+ "max_residuals": MemoryModel.setMaxResiduals,
+ "num_register_banks": MemoryModel.setNumRegisterBanks,
+ "num_registers_per_bank": MemoryModel.setNumRegistersPerBank,
+ "max_hbm_size_in_bytes": MemoryModel.HBM.setMaxCapacity,
+ "max_cache_size_in_bytes": MemoryModel.SPAD.setMaxCapacity,
+ }
+
+ @classmethod
+ def dump_mem_spec_to_json(cls, filename):
+ """
+ Dumps the attributes of all classes as a JSON file under the "mem_spec" section.
+
+ Args:
+ filename (str): The name of the JSON file to write to.
+ """
+
+ # Initialize an empty dictionary to hold all hardware specifications
+ hw_specs = {}
+
+ # Aggregate hardware specifications from each class into a single dictionary
+ hw_specs.update(Constants.hw_spec_as_dict())
+ hw_specs.update(MemoryModel.hw_spec_as_dict())
+ hw_specs.update(MemoryModel.HBM.hw_spec_as_dict())
+ hw_specs.update(MemoryModel.SPAD.hw_spec_as_dict())
+
+ # Wrap the hw_specs in a top-level dictionary
+ output_dict = {"mem_spec": hw_specs}
+
+ # Write the dictionary to a JSON file
+ with open(filename, 'w') as json_file:
+ json.dump(output_dict, json_file, indent=4)
+
+
+ @classmethod
+ def init_mem_spec_from_json(cls, filename):
+ """
+ Updates class attributes using methods specified in the target_attributes dictionary based on a JSON file.
+ This method checks wether values found on json file exists in target dictionaries.
+
+ Args:
+ filename (str): The name of the JSON file to read from.
+ """
+ with open(filename, 'r') as json_file:
+ data = json.load(json_file)
+
+ # Check for the "mem_spec" section
+ if "mem_spec" not in data:
+ raise ValueError("The JSON file does not contain the 'mem_spec' section.")
+
+ mem_spec = data["mem_spec"]
+
+ # Check for missing attributes
+ missing_keys = set(cls._target_attributes.keys()) - set(mem_spec.keys())
+ if missing_keys:
+ raise ValueError(f"The JSON file is missing the following attributes: {', '.join(missing_keys)}")
+
+ # Internal function to convert size expressions to bytes
+ def parse_size_expression(value):
+ size_map = {
+ 'kb': Constants.KILOBYTE,
+ 'mb': Constants.MEGABYTE,
+ 'gb': Constants.GIGABYTE,
+ 'kib': Constants.KILOBYTE,
+ 'mib': Constants.MEGABYTE,
+ 'gib': Constants.GIGABYTE,
+ 'b': 1
+ }
+ value = value.strip()
+ match = re.match(r'^\s*(\d+(\.\d+)?)\s*(b|kb|mb|gb|tb|kib|mib|gib|tib)?\s*$', value.lower())
+ if not match:
+ raise ValueError(f"Invalid size expression: {value}")
+ number, _, unit = match.groups()
+ unit = unit or 'b' # Default to bytes if no unit is specified
+ return int(float(number) * size_map[unit])
+
+ for key, value in mem_spec.items():
+ if key not in cls._target_attributes:
+ raise ValueError(f"Attribute key '{key}' is not valid.")
+ else:
+ # Convert value to bytes if necessary
+ if 'bytes' in key:
+ value = parse_size_expression(str(value))
+ update_method = cls._target_attributes[key]
+ update_method(value)
+
+ @classmethod
+ def initialize_mem_spec(cls, module_dir, mem_spec_file):
+
+ if not mem_spec_file:
+ mem_spec_file = os.path.join(module_dir, "config/mem_spec.json")
+ mem_spec_file = os.path.abspath(mem_spec_file)
+
+ if not os.path.exists(mem_spec_file):
+ raise FileNotFoundError(
+ f"Required Mem Spec file not found: {mem_spec_file}\n"
+ "Please provide a valid path using the `mem_spec` option, "
+ "or use a valid default file at: `/config/mem_spec.json`."
+ )
+
+ cls.init_mem_spec_from_json(mem_spec_file)
+
+ return mem_spec_file
diff --git a/assembler_tools/hec-assembler-tools/assembler/stages/asm_scheduler.py b/assembler_tools/hec-assembler-tools/assembler/stages/asm_scheduler.py
index 29e38374..c895dbe8 100644
--- a/assembler_tools/hec-assembler-tools/assembler/stages/asm_scheduler.py
+++ b/assembler_tools/hec-assembler-tools/assembler/stages/asm_scheduler.py
@@ -204,8 +204,6 @@ class Simulation:
INSTRUCTION_WINDOW_SIZE = 100
MIN_INSTRUCTIONS_IN_TOPO_SORT = 10
- # Amount of instructions for a bundle to be considered short
- BUNDLE_INSTRUCTION_MIN_LIMIT = Constants.MAX_BUNDLE_SIZE // 4 # 10
def __init__(self,
dependency_graph: nx.DiGraph,
@@ -351,6 +349,11 @@ def __init__(self,
self.scheduled_xinsts_count = 0
self.verbose = progress_verbose
+ # Amount of instructions for a bundle to be considered short
+ @property
+ def BUNDLE_INSTRUCTION_MIN_LIMIT(self):
+ return Constants.MAX_BUNDLE_SIZE // 4
+
@property
def last_xinstr(self) -> object:
"""
diff --git a/assembler_tools/hec-assembler-tools/config/mem_spec.json b/assembler_tools/hec-assembler-tools/config/mem_spec.json
new file mode 100644
index 00000000..c417d12c
--- /dev/null
+++ b/assembler_tools/hec-assembler-tools/config/mem_spec.json
@@ -0,0 +1,21 @@
+{
+ "mem_spec": {
+ "max_cache_size_in_bytes": "64 MiB",
+ "max_hbm_size_in_bytes": "64 GiB",
+ "max_xinst_queue_size_in_bytes": "1 MiB",
+ "max_cinst_queue_size_in_bytes": "128 KiB",
+ "max_minst_queue_size_in_bytes": "128 KiB",
+ "max_store_buffer_size_in_bytes": "128 KiB",
+ "num_register_banks": 4,
+ "num_registers_per_bank": 72,
+ "bytes_per_xinstruction": 8,
+ "max_instructions_per_bundle": 64,
+ "num_blocks_per_twid_meta_word": 4,
+ "num_blocks_per_kgseed_meta_word": 4,
+ "num_routing_table_registers": 1,
+ "num_ones_meta_registers": 1,
+ "num_twiddle_meta_registers": 32,
+ "twiddle_meta_register_size_in_bytes": "8 KiB",
+ "max_residuals": 64
+ }
+}
diff --git a/assembler_tools/hec-assembler-tools/he_as.py b/assembler_tools/hec-assembler-tools/he_as.py
index 2ee92c49..1facbf66 100644
--- a/assembler_tools/hec-assembler-tools/he_as.py
+++ b/assembler_tools/hec-assembler-tools/he_as.py
@@ -29,13 +29,13 @@
import time
from assembler.common.run_config import RunConfig
-from assembler.common.run_config import static_initializer
from assembler.common import constants
from assembler.common import makeUniquePath
from assembler.common.config import GlobalConfig
from assembler.common.counter import Counter
-from assembler.isa_spec import SpecConfig
+from assembler.spec_config.isa_spec import ISASpecConfig
+from assembler.spec_config.mem_spec import MemSpecConfig
from assembler.instructions import xinst
from assembler.stages import scheduler
from assembler.stages.asm_scheduler import scheduleASMISAInstructions
@@ -50,7 +50,6 @@
DEFAULT_MINST_FILE_EXT = "minst"
DEFAULT_MEM_FILE_EXT = "mem"
-@static_initializer
class AssemblerRunConfig(RunConfig):
"""
Maintains the configuration data for the run.
@@ -93,15 +92,19 @@ def __init__(self, **kwargs):
At least, one of the arguments passed is invalid.
"""
- super().__init__(**kwargs)
-
+ self.init_default_config()
# class members based on configuration
for config_name, default_value in self.__default_config.items():
- assert(not hasattr(self, config_name))
- setattr(self, config_name, kwargs.get(config_name, default_value))
- if getattr(self, config_name) is None:
- raise TypeError(f'Expected value for configuration `{config_name}`, but `None` received.')
+ value = kwargs.get(config_name)
+ if value is not None:
+ assert(not hasattr(self, config_name))
+ setattr(self, config_name, value)
+ else:
+ if not hasattr(self, config_name):
+ setattr(self, config_name, default_value)
+ if getattr(self, config_name) is None:
+ raise TypeError(f'Expected value for configuration `{config_name}`, but `None` received.')
# class members
self.input_prefix = ""
@@ -122,16 +125,22 @@ def __init__(self, **kwargs):
self.input_mem_file = makeUniquePath(self.input_mem_file)
@classmethod
- def init_static(cls):
+ def init_default_config(cls):
"""
Initializes static members of the class.
"""
if not cls.__initialized:
- cls.__default_config["input_file"] = None
- cls.__default_config["input_mem_file"] = ""
- cls.__default_config["output_dir"] = ""
- cls.__default_config["output_prefix"] = ""
- cls.__default_config["has_hbm"] = True
+ cls.__default_config["input_file"] = None
+ cls.__default_config["input_mem_file"] = ""
+ cls.__default_config["output_dir"] = ""
+ cls.__default_config["output_prefix"] = ""
+ cls.__default_config["has_hbm"] = True
+ cls.__default_config["hbm_size"] = cls.DEFAULT_HBM_SIZE_KB
+ cls.__default_config["spad_size"] = cls.DEFAULT_SPAD_SIZE_KB
+ cls.__default_config["repl_policy"] = cls.DEFAULT_REPL_POLICY
+ cls.__default_config["use_xinstfetch"] = GlobalConfig.useXInstFetch
+ cls.__default_config["suppress_comments"] = GlobalConfig.suppressComments
+ cls.__default_config["debug_verbose"] = GlobalConfig.debugVerbose
cls.__initialized = True
def __str__(self):
@@ -316,7 +325,7 @@ def main(config: AssemblerRunConfig, verbose: bool = False):
GlobalConfig.useHBMPlaceHolders = True #config.use_hbm_placeholders
GlobalConfig.useXInstFetch = config.use_xinstfetch
- GlobalConfig.supressComments = config.suppress_comments
+ GlobalConfig.suppressComments = config.suppress_comments
GlobalConfig.hasHBM = config.has_hbm
GlobalConfig.debugVerbose = config.debug_verbose
@@ -358,6 +367,8 @@ def parse_args():
"File must be the result of pre-processing a P-ISA kernel with he_prep.py"))
parser.add_argument("--isa_spec", default="", dest="isa_spec_file",
help=("Input ISA specification (.json) file."))
+ parser.add_argument("--mem_spec", default="", dest="mem_spec_file",
+ help=("Input Mem specification (.json) file."))
parser.add_argument("--input_mem_file", default="", help=("Input memory mapping file associated with the kernel. "
"Defaults to the same name as the input file, but with `.mem` extension."))
parser.add_argument("--output_dir", default="", help=("Directory where to store all intermediate files and final output. "
@@ -365,21 +376,17 @@ def parse_args():
"Defaults to the same directory as the input file."))
parser.add_argument("--output_prefix", default="", help=("Prefix for the output files. "
"Defaults to the same the input file without extension."))
- parser.add_argument("--spad_size", type=int, default=AssemblerRunConfig.DEFAULT_SPAD_SIZE_KB,
- help="Scratchpad size in KB. Defaults to {} KB.".format(AssemblerRunConfig.DEFAULT_SPAD_SIZE_KB))
- parser.add_argument("--hbm_size", type=int, default=AssemblerRunConfig.DEFAULT_HBM_SIZE_KB,
- help="HBM size in KB. Defaults to {} KB.".format(AssemblerRunConfig.DEFAULT_HBM_SIZE_KB))
+ parser.add_argument("--spad_size", type=int, help="Scratchpad size in KB.")
+ parser.add_argument("--hbm_size", type=int, help="HBM size in KB.")
parser.add_argument("--no_hbm", dest="has_hbm", action="store_false",
help="If set, this flag tells he_prep there is no HBM in the target chip.")
- parser.add_argument("--repl_policy", default=AssemblerRunConfig.DEFAULT_REPL_POLICY,
- choices=constants.Constants.REPLACEMENT_POLICIES,
- help="Replacement policy for cache evictions. Defaults to {}.".format(AssemblerRunConfig.DEFAULT_REPL_POLICY))
+ parser.add_argument("--repl_policy", choices=constants.Constants.REPLACEMENT_POLICIES,
+ help="Replacement policy for cache evictions.")
parser.add_argument("--use_xinstfetch", dest="use_xinstfetch", action="store_true",
help=("When enabled, `xinstfetch` instructions are generated in the CInstQ."))
parser.add_argument("--suppress_comments", "--no_comments", dest="suppress_comments", action="store_true",
help=("When enabled, no comments will be emited on the output generated by the assembler."))
- parser.add_argument("--debug_verbose", type=int, default=0)
- parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0,
+ parser.add_argument("-v", "--verbose", dest="debug_verbose", action="count", default=0,
help=("If enabled, extra information and progress reports are printed to stdout. "
"Increase level of verbosity by specifying flag multiple times, e.g. -vv"))
args = parser.parse_args()
@@ -390,12 +397,14 @@ def parse_args():
module_dir = os.path.dirname(__file__)
module_name = os.path.basename(__file__)
+ # Initialize Defaults
args = parse_args()
- args.isa_spec_file = SpecConfig.initialize_isa_spec(module_dir, args.isa_spec_file)
+ args.isa_spec_file = ISASpecConfig.initialize_isa_spec(module_dir, args.isa_spec_file)
+ args.mem_spec_file = MemSpecConfig.initialize_mem_spec(module_dir, args.mem_spec_file)
config = AssemblerRunConfig(**vars(args)) # convert argsparser into a dictionary
- if args.verbose > 0:
+ if args.debug_verbose > 0:
print(module_name)
print()
print("Run Configuration")
@@ -404,8 +413,8 @@ def parse_args():
print("=================")
print()
- main(config, verbose = args.verbose > 1)
+ main(config, verbose = args.debug_verbose > 1)
- if args.verbose > 0:
+ if args.debug_verbose > 0:
print()
print(module_name, "- Complete")
diff --git a/assembler_tools/hec-assembler-tools/he_link.py b/assembler_tools/hec-assembler-tools/he_link.py
index 880f6c5f..2560ad99 100644
--- a/assembler_tools/hec-assembler-tools/he_link.py
+++ b/assembler_tools/hec-assembler-tools/he_link.py
@@ -38,15 +38,14 @@
from assembler.common import makeUniquePath
from assembler.common.counter import Counter
from assembler.common.run_config import RunConfig
-from assembler.common.run_config import static_initializer
from assembler.common.config import GlobalConfig
from assembler.memory_model import mem_info
-from assembler.isa_spec import SpecConfig
+from assembler.spec_config.mem_spec import MemSpecConfig
+from assembler.spec_config.isa_spec import ISASpecConfig
from linker import loader
from linker.steps import variable_discovery
from linker.steps import program_linker
-@static_initializer
class LinkerRunConfig(RunConfig):
"""
Maintains the configuration data for the run.
@@ -93,22 +92,26 @@ def __init__(self, **kwargs):
At least, one of the arguments passed is invalid.
"""
- super().__init__(**kwargs)
-
-
+ self.init_default_config()
+
# class members based on configuration
for config_name, default_value in self.__default_config.items():
- assert(not hasattr(self, config_name))
- setattr(self, config_name, kwargs.get(config_name, default_value))
- if getattr(self, config_name) is None:
- raise TypeError(f'Expected value for configuration `{config_name}`, but `None` received.')
+ value = kwargs.get(config_name)
+ if value is not None:
+ assert(not hasattr(self, config_name))
+ setattr(self, config_name, value)
+ else:
+ if not hasattr(self, config_name):
+ setattr(self, config_name, default_value)
+ if getattr(self, config_name) is None:
+ raise TypeError(f'Expected value for configuration `{config_name}`, but `None` received.')
# fix file names
self.output_dir = makeUniquePath(self.output_dir)
self.input_mem_file = makeUniquePath(self.input_mem_file)
@classmethod
- def init_static(cls):
+ def init_default_config(cls):
"""
Initializes static members of the class.
"""
@@ -118,6 +121,9 @@ def init_static(cls):
cls.__default_config["output_dir"] = os.getcwd()
cls.__default_config["output_prefix"] = None
cls.__default_config["has_hbm"] = True
+ cls.__default_config["hbm_size"] = cls.DEFAULT_HBM_SIZE_KB
+ cls.__default_config["use_xinstfetch"] = GlobalConfig.useXInstFetch
+ cls.__default_config["suppress_comments"] = GlobalConfig.suppressComments
cls.__initialized = True
@@ -328,6 +334,8 @@ def parse_args():
help=("List of input prefixes, including full path. For an input prefix, linker will "
"assume three files exist named `input_prefixes[i] + '.minst'`, "
"`input_prefixes[i] + '.cinst'`, and `input_prefixes[i] + '.xinst'`."))
+ parser.add_argument("--mem_spec", default="", dest="mem_spec_file",
+ help=("Input Mem specification (.json) file."))
parser.add_argument("--isa_spec", default="", dest="isa_spec_file",
help=("Input ISA specification (.json) file."))
parser.add_argument("-im", "--input_mem_file", dest="input_mem_file", required=True,
@@ -346,8 +354,7 @@ def parse_args():
help=("Directory where to store all intermediate files and final output. "
"This will be created if it doesn't exists. "
"Defaults to current working directory."))
- parser.add_argument("--hbm_size", type=int, default=LinkerRunConfig.DEFAULT_HBM_SIZE_KB,
- help="HBM size in KB. Defaults to {} KB.".format(LinkerRunConfig.DEFAULT_HBM_SIZE_KB))
+ parser.add_argument("--hbm_size", type=int, help="HBM size in KB.")
parser.add_argument("--no_hbm", dest="has_hbm", action="store_false",
help="If set, this flag tells he_prep there is no HBM in the target chip.")
parser.add_argument("--suppress_comments", "--no_comments", dest="suppress_comments", action="store_true",
@@ -364,7 +371,8 @@ def parse_args():
module_name = os.path.basename(__file__)
args = parse_args()
- args.isa_spec_file = SpecConfig.initialize_isa_spec(module_dir, args.isa_spec_file)
+ args.mem_spec_file = MemSpecConfig.initialize_mem_spec(module_dir, args.mem_spec_file)
+ args.isa_spec_file = ISASpecConfig.initialize_isa_spec(module_dir, args.isa_spec_file)
config = LinkerRunConfig(**vars(args)) # convert argsparser into a dictionary
if args.verbose > 0:
diff --git a/assembler_tools/hec-assembler-tools/he_prep.py b/assembler_tools/hec-assembler-tools/he_prep.py
index 7c207e99..234058cf 100644
--- a/assembler_tools/hec-assembler-tools/he_prep.py
+++ b/assembler_tools/hec-assembler-tools/he_prep.py
@@ -23,7 +23,8 @@
import time
from assembler.common import constants
-from assembler.isa_spec import SpecConfig
+from assembler.spec_config.isa_spec import ISASpecConfig
+from assembler.spec_config.mem_spec import MemSpecConfig
from assembler.stages import preprocessor
from assembler.memory_model import MemoryModel
@@ -76,7 +77,8 @@ def main(output_file_name: str,
output_file_name = ''.join(output_file_name[:-1] + (".tw",) + output_file_name[-1:])
hec_mem_model = MemoryModel(constants.MemoryModel.HBM.MAX_CAPACITY_WORDS,
- constants.MemoryModel.SPAD.MAX_CAPACITY_WORDS)
+ constants.MemoryModel.SPAD.MAX_CAPACITY_WORDS,
+ constants.MemoryModel.NUM_REGISTER_BANKS)
insts_listing = []
start_time = time.time()
@@ -124,6 +126,8 @@ def parse_args():
parser.add_argument("output_file_name", nargs="?", help="Output file name. Defaults to .tw.")
parser.add_argument("--isa_spec", default="", dest="isa_spec_file",
help=("Input ISA specification (.json) file."))
+ parser.add_argument("--mem_spec", default="", dest="mem_spec_file",
+ help=("Input Mem specification (.json) file."))
parser.add_argument("-v", "--verbose", dest="verbose", action="count", default=0,
help=("If enabled, extra information and progress reports are printed to stdout. "
"Increase level of verbosity by specifying flag multiple times, e.g. -vv"))
@@ -137,7 +141,8 @@ def parse_args():
args = parse_args()
- args.isa_spec_file = SpecConfig.initialize_isa_spec(module_dir, args.isa_spec_file)
+ args.isa_spec_file = ISASpecConfig.initialize_isa_spec(module_dir, args.isa_spec_file)
+ args.mem_spec_file = MemSpecConfig.initialize_mem_spec(module_dir, args.mem_spec_file)
if args.verbose > 0:
print(module_name)
@@ -145,6 +150,7 @@ def parse_args():
print("Input: {0}".format(args.input_file_name))
print("Output: {0}".format(args.output_file_name))
print("ISA Spec: {0}".format(args.isa_spec_file))
+ print("Mem Spec: {0}".format(args.mem_spec_file))
main(output_file_name=args.output_file_name,
input_file_name=args.input_file_name,