text: from math import ceil
  from myhdl import intbv
  
  class ControllerSpec():
      '''
      address-type message layout
       - width_opcode: 8
       - width_addr:  32
       - width_data:   8
      
      47            40 39             \   \    8 7             0
       |----opcode---| |----address---/   /----| |-----data----|
       |             | |              \   \    | |             |
      |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-/   /-|-|-|-|-|-|-|-|-|-|-|
                                      \   \
  
      value-type message layout
       - width_opcode: 8
       - width_addr:  40
  
      47            40 39                           \   \      0
       |----opcode---| |----value-------------------/   /------|
       |             | |                            \   \      |
      |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-/   /-|-|-|-|
                                                    \   \
      '''
      _WIDTH_BYTE = 8
      # opcode width = 4 => 2^4=16 opcodes possible
      _WIDTH_OPCODE = 4
  
      _OPCODE_CMD_READ = 0
      # - addr
      _OPCODE_CMD_WRITE = 1
      # - addr
      # - data
      _OPCODE_CMD_RESET = 2
      _OPCODE_CMD_STEP = 3
      _OPCODE_CMD_START = 4
      _OPCODE_CMD_PAUSE = 5
      _OPCODE_CMD_STATUS = 6
  
      _OPCODE_RES_READ_SUCCESS = 0
      # - addr
      # - data
      _OPCODE_RES_READ_ERROR_MODE = 1
      # - addr
      _OPCODE_RES_WRITE_SUCCESS = 2
      # - addr
      # - data
      _OPCODE_RES_WRITE_ERROR_MODE = 3
      # - addr
      # - data
      _OPCODE_RES_RESET_SUCCESS = 4
      _OPCODE_RES_STEP_SUCCESS = 5
      # - value (cycle count after step)
      _OPCODE_RES_STEP_ERROR_MODE = 6
      _OPCODE_RES_START_SUCCESS = 7
      # - value (cycle count before start)
      _OPCODE_RES_START_ERROR_MODE = 8
      _OPCODE_RES_PAUSE_SUCCESS = 9
      # - value (cycle count after pause)
      _OPCODE_RES_PAUSE_ERROR_MODE = 10
      _OPCODE_RES_STATUS = 11 
      # - value (mode + cycle count)
  
      _ADDR_TYPE_CMD_OPCODES = [_OPCODE_CMD_READ, _OPCODE_CMD_WRITE] 
      _ADDR_TYPE_RES_OPCODES = [_OPCODE_RES_READ_SUCCESS, 
              _OPCODE_RES_READ_ERROR_MODE, _OPCODE_RES_WRITE_SUCCESS, 
              _OPCODE_RES_WRITE_ERROR_MODE]
  
      _CHR_START = 0x12
      _CHR_STOP = 0x13
      _CHR_ESC = 0x7D
  
      def __init__(self, width_addr, width_data):
  
          if width_addr < 1:
              raise ValueError('address width must be at least 1')
          if width_data < 1:
              raise ValueError('data width must be at least 1')
  
          self._width_addr = width_addr
          self._width_data = width_data
  
      @property
      def width_opcode(self):
          return self._WIDTH_OPCODE
  
      @property
      def width_addr(self):
          return self._width_addr
  
      @property
      def width_data(self):
          return self._width_data
  
      @property
      def width_value(self):
          return self.width_addr + self.width_data
  
      @property
      def width_message(self):
          return self.width_opcode + self.width_value
  
      @property
      def width_message_bytes(self):
          return int(ceil(float(self.width_message) / 
              float(self._WIDTH_BYTE)))
  
      @property
      def index_opcode_high(self):
          return self.width_message - 1
  
      @property
      def index_opcode_low(self):
          return self.width_message - self.width_opcode
  
      @property
      def index_addr_high(self):
          return self.width_message - self.width_opcode - 1
  
      @property
      def index_addr_low(self):
          return self.width_message - self.width_opcode - self.width_addr
  
      @property
      def index_data_high(self):
          return self.width_data - 1
  
      @property
      def index_data_low(self):
          return 0
  
      @property
      def index_value_high(self):
          return self.width_value - 1
  
      @property
      def index_value_low(self):
          return 0
  
      #Command opcodes
      @property
      def opcode_cmd_read(self):
          return self._OPCODE_CMD_READ
  
      @property
      def opcode_cmd_write(self):
          return self._OPCODE_CMD_WRITE
  
      @property
      def opcode_cmd_reset(self):
          return self._OPCODE_CMD_RESET
  
      @property
      def opcode_cmd_step(self):
          return self._OPCODE_CMD_STEP
  
      @property
      def opcode_cmd_start(self):
          return self._OPCODE_CMD_START
  
      @property
      def opcode_cmd_pause(self):
          return self._OPCODE_CMD_PAUSE
  
      @property
      def opcode_cmd_status(self):
          return self._OPCODE_CMD_STATUS
      
      # Repsonse opcodes
      @property
      def opcode_res_read_success(self):
          return self._OPCODE_RES_READ_SUCCESS
  
      @property
      def opcode_res_read_error_mode(self):
          return self._OPCODE_RES_READ_ERROR_MODE
  
      @property
      def opcode_res_write_success(self):
          return self._OPCODE_RES_WRITE_SUCCESS
  
      @property
      def opcode_res_write_error_mode(self):
          return self._OPCODE_RES_WRITE_ERROR_MODE
  
      @property
      def opcode_res_reset_success(self):
          return self._OPCODE_RES_RESET_SUCCESS
  
      @property
      def opcode_res_step_success(self):
          return self._OPCODE_RES_STEP_SUCCESS
  
      @property
      def opcode_res_step_error_mode(self):
          return self._OPCODE_RES_STEP_ERROR_MODE
  
      @property
      def opcode_res_start_success(self):
          return self._OPCODE_RES_START_SUCCESS
  
      @property
      def opcode_res_start_error_mode(self):
          return self._OPCODE_RES_START_ERROR_MODE
  
      @property
      def opcode_res_pause_success(self):
          return self._OPCODE_RES_PAUSE_SUCCESS
  
      @property
      def opcode_res_pause_error_mode(self):
          return self._OPCODE_RES_PAUSE_ERROR_MODE
  
      @property
      def opcode_res_status(self):
          return self._OPCODE_RES_STATUS
  
      def is_addr_type_command(self, opcode):
          return (opcode in self._ADDR_TYPE_CMD_OPCODES)
  
      def is_value_type_command(self, opcode):
          return not self.is_addr_type_command(opcode)
  
      def is_addr_type_response(self, opcode):
          return (opcode in self._ADDR_TYPE_RES_OPCODES)
  
      def is_value_type_response(self, opcode):
          return not self.is_addr_type_response(opcode)
  
      def value_type_message(self, opcode, value):
          message = intbv(0)[self.width_message:0]
          message[self.index_opcode_high+1:self.index_opcode_low] = opcode
          message[self.index_value_high+1:self.index_value_low] = value
  
          return int(message)
  
      def addr_type_message(self, opcode, address, data):
          message = intbv(0)[self.width_message:0]
          message[self.index_opcode_high+1:self.index_opcode_low] = opcode
          message[self.index_addr_high+1:self.index_addr_low] = address
          message[self.index_data_high+1:self.index_data_low] = data
  
          return int(message)
  
      def parse_opcode(self, message):
          m = intbv(message)
          return int(m[self.index_opcode_high+1:self.index_opcode_low])
  
      def parse_addr(self, message):
          m = intbv(message)
          return int(m[self.index_addr_high+1:self.index_addr_low])
  
      def parse_data(self, message):
          m = intbv(message)
          return int(m[self.index_data_high+1:self.index_data_low])
  
      def parse_value(self, message):
          m = intbv(message)
          return int(m[self.index_value_high+1:self.index_value_low])
  
      @property
      def chr_start(self):
          return self._CHR_START
  
      @property
      def chr_stop(self):
          return self._CHR_STOP
  
      @property
      def chr_esc(self):
          return self._CHR_ESC
  
  
  