pymtl / pymtl3

Pymtl 3 (Mamba), an open-source Python-based hardware generation, simulation, and verification framework
BSD 3-Clause "New" or "Revised" License
380 stars 48 forks source link

Cycle Level simulation reset function not right #282

Open CaseyZhu opened 1 week ago

CaseyZhu commented 1 week ago

''' from pymtl3 import * from pymtl3.stdlib.queues import PipeQueueCL

定义一个简单的被调用组件

class SimpleCallee(Component): def construct(s):

s.set = CalleePort(Type=Bits32)

    s.send = CallerIfcCL(Type=Bits32)
    s.counter = b32(0)
    print(s.counter)
    #s.set //= s.my_set

    @update_once
    def up_counter_ff():
        if s.send.rdy():
            s.send(s.counter)
            s.counter @= s.counter + b32(1)
    #s.add_constraints(U(up_counter) < U(up_counter_ff))    

def line_trace(s):
    return f"Callee: counter={s.counter}"

class SimpleCaller(Component): def construct(s): s.receive = CalleeIfcCL(Type=Bits32) s.received_data = Wire(32) s.receive //= s.f_receive ''' s.in_q = PipeQueueCL(num_entries=2)

    s.in_q.enq //= s.receive

    @update_once
    def up_receive():
        if s.in_q.deq.rdy():
            s.received_data @= s.in_q.deq()
    '''

@non_blocking( lambda s: True )
def f_receive( s, msg ):
    s.received_data @= msg
    #print(f"Received message: {msg}")

def line_trace(s):
    return f"Caller: received={s.received_data}"

class TopLevel(Component): def construct(s): s.callee = SimpleCallee() s.caller = SimpleCaller() connect(s.callee.send, s.caller.receive)

def line_trace(s):
    return f"{s.callee.line_trace()} -> {s.caller.line_trace()}"

def test_caller_callee(): top = TopLevel()

top.elaborate()

top.apply(DefaultPassGroup())
#top.sim_reset() 这个会占用仿真时间!!(会触发update_once)
print(top.callee.counter)
print("Starting simulation...")
for i in range(10):  # 运行10个周期
    top.sim_tick()
    print(f"Cycle {i}: {top.line_trace()}")

if name == "main": test_caller_callee() ''' the code above will print

00000000
00000000
Starting simulation...
Cycle 0: Callee: counter=00000001 -> Caller: received=00000000
Cycle 1: Callee: counter=00000002 -> Caller: received=00000001
Cycle 2: Callee: counter=00000003 -> Caller: received=00000002
Cycle 3: Callee: counter=00000004 -> Caller: received=00000003
Cycle 4: Callee: counter=00000005 -> Caller: received=00000004
Cycle 5: Callee: counter=00000006 -> Caller: received=00000005
Cycle 6: Callee: counter=00000007 -> Caller: received=00000006
Cycle 7: Callee: counter=00000008 -> Caller: received=00000007
Cycle 8: Callee: counter=00000009 -> Caller: received=00000008
Cycle 9: Callee: counter=0000000a -> Caller: received=00000009

if add sim_reset() the results is wrong ''' 00000000 00000004 Starting simulation... Cycle 0: Callee: counter=00000005 -> Caller: received=00000004 Cycle 1: Callee: counter=00000006 -> Caller: received=00000005 Cycle 2: Callee: counter=00000007 -> Caller: received=00000006 Cycle 3: Callee: counter=00000008 -> Caller: received=00000007 Cycle 4: Callee: counter=00000009 -> Caller: received=00000008 Cycle 5: Callee: counter=0000000a -> Caller: received=00000009 Cycle 6: Callee: counter=0000000b -> Caller: received=0000000a Cycle 7: Callee: counter=0000000c -> Caller: received=0000000b Cycle 8: Callee: counter=0000000d -> Caller: received=0000000c Cycle 9: Callee: counter=0000000e -> Caller: received=0000000d '''

yo96 commented 1 week ago

Hi,

Thank you for using PyMTL3. The issue is that when you call the sim_reset() method, the clock will be advanced, and the first few messages will be transmitted. If you are doing pure CL simulation, sim_reset() is not needed since all it does is just toggling the reset signal and ticking the clock for a few cycles.

If you do want to use the reset signal for some reason, you can either write your own reset function to do what you want or modify your update block to not do anything during reset, for example:

    @update_once
    def up_counter_ff():
        if s.reset:
            s.counter @= 0
        elif s.send.rdy():
            s.send(s.counter)
            s.counter @= s.counter + b32(1)