2

I cannot find an example in the Simics documentation on how the clock object is obtained so that we can use it as an argument in the post() method.

I suspect that either

  • an attribute can be used to get the clock or
  • in the ConfObject class scope we get the clock using SIM_object_clock()

I created a new module using bin\project-setup --py-device event-py

I have defined two methods in the ConfObject class scope called clock_set and clock_get.

I wanted to use these methods so that I can set/get the clock object to use in the post method.

The post() method fails when reading the device registers in the vacuum machine.

import pyobj
# Tie code to specific API, simplifying upgrade to new major version
import simics_6_api as simics


class event_py(pyobj.ConfObject):
    """This is the long-winded documentation for this Simics class.
    It can be as long as you want."""
    _class_desc = "one-line doc for the class"
    _do_not_init = object()

    def _initialize(self):
        super()._initialize()


    def _info(self):
        return []

    def _status(self):
        return [("Registers", [("value", self.value.val)])]

    def getter(self):
        return self

# In my mind, clock_set is supposed to set the clock object. That way we can use
# it in post()
    def clock_set(self):
        self.clock = simics.SIM_object_clock(self)

    def clock_get(self):
        return self.clock(self):

    class value(pyobj.SimpleAttribute(0, 'i')):
        """The <i>value</i> register."""

    class ev1(pyobj.Event):
        def callback(self, data):
            return 'ev1 with %s' % data


    class regs(pyobj.Port):
        class io_memory(pyobj.Interface):
            def operation(self, mop, info):
                offset = (simics.SIM_get_mem_op_physical_address(mop)
                          + info.start - info.base)
                size = simics.SIM_get_mem_op_size(mop)

                if offset == 0x00 and size == 1:
                    if simics.SIM_mem_op_is_read(mop):
                        val = self._up._up.value.val
                        simics.SIM_set_mem_op_value_le(mop, val)
                        # POST HERE AS TEST self._up._up.ev1.post(clock, val, seconds = 1)
                    else:
                        val = simics.SIM_get_mem_op_value_le(mop)
                        self._up._up.value.val = val
                    return simics.Sim_PE_No_Exception
                else:
                    return simics.Sim_PE_IO_Error
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459

2 Answers2

1

You mention using the vacuum example machine and within its script you see that sdp->queue will point to timer. So SIM_object_clock(sdp) would return timer.

Simics is using queue attribute in all conf-objects to reference their clock individually, though other implementations are used too.

BR Simon #IAmIntel

simgron
  • 139
  • 6
1

It seems the pyobj documentation and example Python code aren't in the best shape. Sorry for that, issues have been filed.

Here is a proposed rephrasing of the code that works. Using a port object to map the register. Adding logs on events to make it clear what happens. And posting the event in the way you are supposed to, using the queue attribute of the object.

import pyobj
# Tie code to specific API, simplifying upgrade to new major version
import simics_6_api as simics


class event_py(pyobj.ConfObject):
    """This is the long-winded documentation for this Simics class.
    It can be as long as you want."""
    _class_desc = "one-line doc for the class"
    _do_not_init = object()

    def _initialize(self):
        super()._initialize()


    def _info(self):
        return []

    def _status(self):
        return [("Registers", [("value", self.value.val)])]

    def getter(self):
        return self

    class value(pyobj.SimpleAttribute(0, 'i')):
        """The <i>value</i> register."""

    class ev1(pyobj.Event):
        def callback(self, data):
            ## When event triggers, log 
            simics.SIM_log_info(1, self._up.obj, 0, 
                   f"Event callback called, data={data}")

    ## Use PortObject to get a modern-style port for the bank
    class regs(pyobj.PortObject):
        ## Register banks should go into the bank namespace
        ## Resulting port object is called <dev>.bank.regs
        namespace="bank"
        class io_memory(pyobj.Interface):
            def operation(self, mop, info):
                offset = (simics.SIM_get_mem_op_physical_address(mop)
                          + info.start - info.base)
                size = simics.SIM_get_mem_op_size(mop)

                ## Make the register 4 bytes to be slightly more interesting
                if offset == 0x00 and size == 4:
                    if simics.SIM_mem_op_is_read(mop):
                        val = self._up._up.value.val
                        simics.SIM_set_mem_op_value_le(mop, val)
                    else:
                        ## Update stored register value
                        val = simics.SIM_get_mem_op_value_le(mop)
                        self._up._up.value.val = val
                        ## Post event on write
                        simics.SIM_log_info(1, self._up._up.obj, 0, f"Posting callback with data={val}")
                        self._up._up.ev1.post(self._up._up.obj.queue, val, cycles = 10)

                    return simics.Sim_PE_No_Exception
                else:
                    return simics.Sim_PE_IO_Error

To use this, you need to have an actual clock object to serve as the queue. I used this code to set up a small test system with a clock, an instance of this class, and a memory map. Put this into a separate Python file and run it when starting Simics.

import simics

def create_test_subsystem(name="test"):
    n = simics.pre_conf_object(name, 'namespace')
    
    # Clock
    n.clock = simics.pre_conf_object('clock', freq_mhz = 1000)

    # Device
    n.dut = simics.pre_conf_object('event_py')

    ## Set the queue attribute of the device to point at the clock
    ## This is how the device finds the clock to post on
    ## All Simics objects have a "queue" attribute, it is part of the
    ## framework.
    n.dut.attr.queue = n.clock

    # Memory map
    n.memmap = simics.pre_conf_object('memory-space',map=[])
    n.memmap.attr.map.append( [0x1000, n.dut.bank.regs, 0, 0, 0x1000] )

    simics.SIM_add_configuration([n],None)

create_test_subsystem()

Now, start a new Simics:

C:...> .\simics.bat test_event_py.py

In Simics, we should get this session:

simics> list-objects -tree
┐
├ bp ┐
│    ├ bank
│    ├ console_string
│    ├ control_register
│    ├ cycle
│    ├ cycle_event
│    ├ exception
│    ├ gfx
│    ├ hap
│    ├ log
│    ├ magic
│    ├ memory
│    ├ notifier
│    ├ os_awareness
│    ├ source_line
│    ├ source_location
│    ├ step
│    ├ step_event
│    └ time
├ breakpoints
├ default_cell0 ┐
│               └ ps
├ default_sync_domain
├ params
├ prefs
├ sim ┐
│     ├ cli
│     ├ cmdline
│     ├ gui
│     ├ rexec
│     ├ tlmtry
│     └ transactions
└ test ┐
       ├ clock ┐
       │       └ vtime ┐
       │               ├ cycles
       │               └ ps
       ├ dut ┐
       │     └ bank ┐
       │            └ regs
       └ memmap

simics> test.memmap.map
┌──────┬──────────────────┬──┬──────┬──────┬──────┬────┬─────┬────┐
│  Base│Object            │Fn│Offset│Length│Target│Prio│Align│Swap│
├──────┼──────────────────┼──┼──────┼──────┼──────┼────┼─────┼────┤
│0x1000│test.dut.bank.regs│  │0x0000│0x1000│      │   0│    8│    │
└──────┴──────────────────┴──┴──────┴──────┴──────┴────┴─────┴────┘
simics> log-setup -time-stamp
simics> peq

simics> test.memmap.write 0x1000 1000 4 -l
[test.dut info] {test.clock 0} Posting callback with data=1000
simics> peq
┌─────┬────────┬───────────┐
│Cycle│ Object │Description│
├─────┼────────┼───────────┤
│   10│test.dut│ev1        │
└─────┴────────┴───────────┘
simics> run 5 cycles
simics> test.memmap.write 0x1000 2000 4 -l
[test.dut info] {test.clock 5} Posting callback with data=2000
simics> peq
┌─────┬────────┬───────────┐
│Cycle│ Object │Description│
├─────┼────────┼───────────┤
│    5│test.dut│ev1        │
│   10│test.dut│ev1        │
└─────┴────────┴───────────┘
simics> run 5 cycles
[test.dut info] {test.clock 10} Event callback called, data=1000
simics> peq
┌─────┬────────┬───────────┐
│Cycle│ Object │Description│
├─────┼────────┼───────────┤
│    5│test.dut│ev1        │
└─────┴────────┴───────────┘
simics>
jakobengblom2
  • 5,531
  • 2
  • 25
  • 33