Skip to content

hyperbus

Initializie Hyperbus library.

Modules:

  • hbc_fsm

    Contains HyperBus_FSM class for the State Machine in hyperbus protocol.

  • hyperbus_controller

    HyperBusController class.

Classes:

HyperBusController

HyperBusController(dut: SimHandleBase)

Bases: HyperBus_FSM

HyperBus Controller class with methods.

Reset, ReadReg, WriteReg, ReadMem, WriteMem, clk_cycle, ck_cycle, int_to_8bit_array, arr_io_dq, Assign, wait_100ns, drive_dq, monitor_dq, wait_until_mem_ready.

Methods:

  • Assign

    Drives and Monitors signals between controllor and dut.

  • Init

    Initialize clock, Controller FSM and connects dut with controllor.

  • ReadMem

    Reads the memory content form the starting address for a specified number of bytes.

  • ReadReg

    Reads from a register based on the address, returns 2 bytes.

  • Reset

    Reset based on timing requirement of the rtl model.

  • WriteMem

    Writes the memory content form the starting address for specified bytes of data.

  • WriteReg

    Writes 2 bytes to a register based on the address.

  • arr_io_dq

    Returns the bit at an index for the 8 bit array.

  • ca_words

    Splits the 48bit CA into 6 bytes for transmission.

  • ck_cycle

    Updates the derived clock based on reset and chip select lines.

  • clk_cycle

    Generate the clock for the test.

  • drive_dq

    Drives the data bus.

  • fsm

    FSM for states IDLE, CAs, WR_LATENCY, WRITE, READ, DONE.

  • fsm_reset

    Resets the fsm to IDLE state.

  • generate_random_data

    Generate random num number of byte.

  • get_time

    Get simulation time.

  • int_to_8bit_array

    Converts Integer into a 8 bit binary string array.

  • is_rwdsvalid

    Samples the RWDS input signal every 5ns and stores it in rwds_d.

  • log

    Logs a message value with prefixed simulation time.

  • monitor_dq

    Moniters the data bus.

  • rwds_valid

    Returns True if RWDS is valid.

  • rx_data

    Returns hex string of a 32 bit number.

  • swap_halves

    Swaps the upper and lower 16 bits of a 32-bit value used for word alignment, endianess conversion into little endian.

  • update_ca

    Builds the 48-bit command/address word using write strobe, config access, and memory address fields.

  • wait_100ns

    Waits for 100ns.

  • wait_until_mem_ready

    Continously polls for every half clock cycle to check if the memory is ready.

  • wdata_words

    Splits the 32 bit write data into bytes, with special arrangement if CA[46]=1.

  • wstrb_words

    Splits Write strobe into bits for each byte lane.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
16
17
18
19
def __init__(self, dut: SimHandleBase) -> None:
    """Initialization."""
    super().__init__()
    self.Init(dut)

Assign async

Assign(dut: SimHandleBase) -> None

Drives and Monitors signals between controllor and dut.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
async def Assign(self, dut: SimHandleBase) -> None:
    """Drives and Monitors signals between controllor and dut."""
    # dut.rwds.value=BinaryValue(self.highimp_1)
    self.drive_dq(dut, self.highimp_8)
    while True:
        if self.o_dq_de:
            self.drive_dq(dut, self.io_dq)

        if self.o_rwds_de:
            dut.rwds.value = Force(self.o_rwds)
        else:
            dut.rwds.value = Release()

        dut.csneg.value = self.o_csn0
        dut.ck.value = self.o_clk
        dut.resetneg.value = self.o_resetn
        self.monitor_dq(dut)
        self.i_rwds = dut.rwds.value
        await Timer(1, "ns")

Init

Init(dut: SimHandleBase) -> None

Initialize clock, Controller FSM and connects dut with controllor.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
21
22
23
24
25
26
27
def Init(self, dut: SimHandleBase) -> None:
    """Initialize clock, Controller FSM and connects dut with controllor."""
    self.log("Init...")
    cocotb.start_soon(self.clk_cycle(dut))
    cocotb.start_soon(self.Assign(dut))
    cocotb.start_soon(self.fsm(dut))
    cocotb.start_soon(self.is_rwdsvalid())

ReadMem async

ReadMem(addr: int, count: int) -> bytes

Reads the memory content form the starting address for a specified number of bytes.

Each transaction is 4 bytes.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
async def ReadMem(self, addr: int, count: int) -> bytes:
    """Reads the memory content form the starting address for a specified number of bytes.

    Each transaction is 4 bytes.
    """
    r_addr = addr
    r_data = []

    count_32b = count // 4
    count_rem = count % 4
    if count_rem > 0:
        count_32b += 1

    for _i in range(count_32b):
        self.log("--------------------------------------------------")
        self.log("Reading from memory...")
        self.i_cfg_access = 0
        self.i_mem_wstrb = 0
        self.i_mem_addr = r_addr
        self.i_mem_valid = 1
        await Timer(10, "ns")
        self.i_mem_valid = 0
        await self.wait_until_mem_ready()
        r_data.append(self.o_mem_rdata)
        self.log("Read operation complete.")
        self.log(f"Address: {hex(r_addr)}   Data: {hex(self.o_mem_rdata)}")
        self.log("--------------------------------------------------")
        # self.log(f"$$$$$$$$ o_mem_rdata {self.rx_data(self.o_mem_rdata,32)}")
        await Timer(20, "ns")
        r_addr += 2

    # Convert list of 32-bit packets back into bytes (little-endian)
    byte_data = []
    for val in r_data:
        byte_data.append((val >> 0) & 0xFF)
        byte_data.append((val >> 8) & 0xFF)
        byte_data.append((val >> 16) & 0xFF)
        byte_data.append((val >> 24) & 0xFF)

    return bytes(byte_data[:count])

ReadReg async

ReadReg(addr: int) -> bytes

Reads from a register based on the address, returns 2 bytes.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
40
41
42
43
44
45
46
47
48
49
50
51
52
async def ReadReg(self, addr: int) -> bytes:
        """Reads from a register based on the address, returns 2 bytes."""
        self.i_cfg_access = 1
        self.i_mem_valid = 1
        self.i_mem_wstrb = 0
        self.i_mem_addr = addr
        await Timer(10, "ns")
        self.i_mem_valid = 0
        await self.wait_until_mem_ready()
        await Timer(20, "ns")

        reg_value = self.mem_rdata & 0xFFFF  # Directly use as int
        return reg_value.to_bytes(2, byteorder="big")  # or 'little' if system is little-endian

Reset async

Reset(dut: SimHandleBase) -> None

Reset based on timing requirement of the rtl model.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
30
31
32
33
34
35
36
37
38
async def Reset(self, dut: SimHandleBase) -> None:
    """Reset based on timing requirement of the rtl model."""
    self.i_rstn = 0
    await Timer(100, "ns")
    self.i_rstn = 1
    self.log("Waiting for device power-up...")
    # TODO: Change power-up time in rtl
    await Timer(160, "ns")
    self.log("RAM IS READY!!!!!!")

WriteMem async

WriteMem(addr: int, data: bytes) -> None

Writes the memory content form the starting address for specified bytes of data.

Each transaction is 4 bytes.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
async def WriteMem(self, addr: int, data: bytes) -> None:
    """Writes the memory content form the starting address for specified bytes of data.

    Each transaction is 4 bytes.
    """
    w_addr = addr

    # Convert byte stream into list of 32-bit packets (little-endian)
    padded_data = data
    if (len(data) % 4) != 0:
        padding = 4 - (len(data) % 4)
        padded_data = data + b"\x00" * padding

    b32_list = []
    for i in range(0, len(padded_data), 4):
        val = (
            (padded_data[i + 0])
            | (padded_data[i + 1] << 8)
            | (padded_data[i + 2] << 16)
            | (padded_data[i + 3] << 24)
        )
        b32_list.append(val)

    for w_data in b32_list:
        self.log("--------------------------------------------------")
        self.log("Writing into memory...")
        self.i_cfg_access = 0
        self.i_mem_wdata = self.swap_halves(w_data)
        self.i_mem_addr = w_addr
        self.i_mem_valid = 1
        self.i_mem_wstrb = 15
        await Timer(10, "ns")
        self.i_mem_valid = 0

        await self.wait_until_mem_ready()
        self.log("Write operation complete.")
        self.log(f"Address: {hex(w_addr)}   Data: {hex(w_data)}")
        self.log("--------------------------------------------------")
        await Timer(20, "ns")
        w_addr += 2

WriteReg async

WriteReg(addr: int, data: bytes) -> None

Writes 2 bytes to a register based on the address.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
async def WriteReg(self, addr: int, data: bytes) -> None:
    """Writes 2 bytes to a register based on the address."""
    expected_bytes = 2
    if len(data) != expected_bytes:
        raise ValueError("WriteReg expects exactly 2 bytes.")

    int_data = int.from_bytes(data, byteorder="big")  # or 'little' if required
    self.i_cfg_access = 1
    self.i_mem_wdata = self.swap_halves(int_data)
    self.i_mem_addr = addr
    self.i_mem_valid = 1
    self.i_mem_wstrb = 0xF
    await Timer(10, "ns")
    self.i_mem_valid = 0
    await self.wait_until_mem_ready()
    await Timer(20, "ns")

arr_io_dq

arr_io_dq(index: int, value: Union[int, str]) -> str

Returns the bit at an index for the 8 bit array.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
180
181
182
183
def arr_io_dq(self, index: int, value: Union[int, str]) -> str:
    """Returns the bit at an index for the 8 bit array."""
    _io_dq = self.int_to_8bit_array(value)
    return _io_dq[index]

ca_words

ca_words() -> List[int]

Splits the 48bit CA into 6 bytes for transmission.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
176
177
178
def ca_words(self) -> List[int]:
    """Splits the 48bit CA into 6 bytes for transmission."""
    return [(self.ca >> (8 * i)) & 0xFF for i in range(6)]

ck_cycle

ck_cycle(dut: SimHandleBase) -> None

Updates the derived clock based on reset and chip select lines.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
163
164
165
166
167
168
169
def ck_cycle(self, dut: SimHandleBase) -> None:
    """Updates the derived clock based on reset and chip select lines."""
    if not self.i_rstn:
        self.bus_clk = 0
    else:
        self.bus_clk = not self.bus_clk if not self.o_csn0 else 0
    self.o_clk = self.bus_clk

clk_cycle async

clk_cycle(dut: SimHandleBase) -> None

Generate the clock for the test.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
154
155
156
157
158
159
160
161
async def clk_cycle(self, dut: SimHandleBase) -> None:
    """Generate the clock for the test."""
    while True:
        self.i_clk = 0
        await Timer(5, "ns")
        self.i_clk = 1
        await Timer(5, "ns")
        self.ck_cycle(dut)

drive_dq

drive_dq(
    dut: SimHandleBase, value: Union[int, str]
) -> None

Drives the data bus.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
209
210
211
212
213
214
215
216
217
218
def drive_dq(self, dut: SimHandleBase, value: Union[int, str]) -> None:
    """Drives the data bus."""
    dut.dq7.value = BinaryValue(self.arr_io_dq(0, value))
    dut.dq6.value = BinaryValue(self.arr_io_dq(1, value))
    dut.dq5.value = BinaryValue(self.arr_io_dq(2, value))
    dut.dq4.value = BinaryValue(self.arr_io_dq(3, value))
    dut.dq3.value = BinaryValue(self.arr_io_dq(4, value))
    dut.dq2.value = BinaryValue(self.arr_io_dq(5, value))
    dut.dq1.value = BinaryValue(self.arr_io_dq(6, value))
    dut.dq0.value = BinaryValue(self.arr_io_dq(7, value))

fsm async

fsm(dut: SimHandleBase) -> None

FSM for states IDLE, CAs, WR_LATENCY, WRITE, READ, DONE.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
async def fsm(self, dut: SimHandleBase) -> None:
    """FSM for states IDLE, CAs, WR_LATENCY, WRITE, READ, DONE."""
    await Timer(5, "ns")
    while True:
        if not self.i_rstn:
            self.fsm_reset()

        elif self.state == self.IDLE:
            self.mem_ready = 0
            if self.i_mem_valid and not self.mem_ready:
                self.ca = self.update_ca(self.i_mem_wstrb, self.i_cfg_access, self.i_mem_addr)
                self.wdata = self.i_mem_wdata
                self.wstrb = self.i_mem_wstrb
                self.counter = 5
                self.state = self.CAs

        elif self.state == self.CAs:
            if self.counter:
                self.counter -= 1
            elif self.ca >> 47:
                self.counter = 3
                self.state = self.READ
            elif self.ca >> 46 & 1:
                self.counter = 1
                self.state = self.WRITE
            else:
                self.counter = self.WRITE_LATENCY
                self.state = self.WR_LATENCY

        elif self.state == self.WR_LATENCY:
            if self.counter:
                self.counter -= 1
            else:
                self.counter = 3
                self.state = self.WRITE

        elif self.state == self.WRITE:

            if self.counter:
                self.counter -= 1
            else:
                self.state = self.DONE

        elif self.state == self.READ:

            if self.rwds_valid():
                if self.counter == self.RBYTE_3:
                    self.mem_rdata = (self.i_dq << 8) | (
                        self.mem_rdata & 0xFFFF00FF
                    )
                elif self.counter == self.RBYTE_2:
                    self.mem_rdata = (self.i_dq) | (self.mem_rdata & 0xFFFFFF00)
                elif self.counter == self.RBYTE_1:
                    self.mem_rdata = (self.i_dq << 24) | (
                        self.mem_rdata & 0x00FFFFFF
                    )
                elif self.counter == self.RBYTE_0:
                    self.mem_rdata = (self.i_dq << 16) | (
                        self.mem_rdata & 0xFF00FFFF
                    )
                if self.counter:
                    self.counter -= 1
                else:
                    self.state = self.DONE

        elif self.state == self.DONE:
            self.mem_ready = 1
            self.state = self.IDLE

        self.o_csn0 = self.state in [self.IDLE, self.DONE]
        self.o_resetn = self.i_rstn
        self.o_dq = (
            self.ca_words()[self.counter]
            if self.state == self.CAs
            else (
                self.wdata_words()[self.counter] if self.state == self.WRITE else 0
            )
        )
        self.o_rwds = (
            not self.wstrb_words()[self.counter] if self.state == self.WRITE else 0
        )
        self.o_dq_de = self.state in [self.CAs, self.WRITE]
        self.o_rwds_de = self.state == self.WRITE and not (self.ca >> 46 & 1)
        self.o_mem_ready = self.mem_ready
        self.o_mem_rdata = self.mem_rdata
        self.io_dq = self.o_dq if self.o_dq_de else self.highimp_8
        self.io_rwds = (self.o_rwds if self.o_rwds_de else BinaryValue(self.highimp_1))
        await Timer(10, "ns")

fsm_reset

fsm_reset() -> None

Resets the fsm to IDLE state.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
72
73
74
75
76
77
78
def fsm_reset(self) -> None:
    """Resets the fsm to IDLE state."""
    self.ca = 0
    self.state = self.IDLE
    self.mem_ready = 0
    self.mem_rdata = 0
    self.counter = 0

generate_random_data

generate_random_data(num: int) -> bytes

Generate random num number of byte.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
235
236
237
238
239
240
241
242
def generate_random_data(self, num: int) -> bytes:
    """Generate random num number of byte."""
    int_list = []
    for _ in range(num):
        # Generate a random integer within the specified range
        random_int = random.randint(0, 2**8 - 1)
        int_list.append(random_int)
    return bytes(int_list)

get_time

get_time() -> int

Get simulation time.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
197
198
199
def get_time(self) -> int:
    """Get simulation time."""
    return get_sim_time("ns")

int_to_8bit_array

int_to_8bit_array(
    num: Union[int, str],
) -> Union[str, List[str]]

Converts Integer into a 8 bit binary string array.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
172
173
174
175
176
177
178
def int_to_8bit_array(self, num: Union[int, str]) -> Union[str, List[str]]:
    """Converts Integer into a 8 bit binary string array."""
    if num in (self.highimp_1, self.highimp_8):
        return str(num)
    binary_str = bin(int(num))[2:]
    padded_binary_str = binary_str.zfill(8)
    return [str(bit) for bit in padded_binary_str]

is_rwdsvalid async

is_rwdsvalid() -> None

Samples the RWDS input signal every 5ns and stores it in rwds_d.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
191
192
193
194
195
async def is_rwdsvalid(self) -> None:
    """Samples the RWDS input signal every 5ns and stores it in rwds_d."""
    while True:
        await Timer(5, "ns")
        self.rwds_d = self.i_rwds

log

log(msg: str) -> None

Logs a message value with prefixed simulation time.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
201
202
203
def log(self, msg: str) -> None:
    """Logs a message value with prefixed simulation time."""
    cocotb.log.info(f"[{self.get_time()}]  {msg}")

monitor_dq

monitor_dq(dut: SimHandleBase) -> None

Moniters the data bus.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
def monitor_dq(self, dut: SimHandleBase) -> None:
    """Moniters the data bus."""
    arr = [
        dut.dq7.value,
        dut.dq6.value,
        dut.dq5.value,
        dut.dq4.value,
        dut.dq3.value,
        dut.dq2.value,
        dut.dq1.value,
        dut.dq0.value,
    ]
    binary_str = "".join(map(str, arr))

    if any(char == "z" for char in binary_str):
        self.i_dq = 0
    else:
        self.i_dq = int(binary_str, 2)

rwds_valid

rwds_valid() -> int

Returns True if RWDS is valid.

Used for data sampling.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
169
170
171
172
173
174
def rwds_valid(self) -> int:
    """Returns True if RWDS is valid.

    Used for data sampling.
    """
    return self.rwds_d or self.i_rwds

rx_data

rx_data(num: int, size: int)

Returns hex string of a 32 bit number.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
205
206
207
208
209
210
def rx_data(self, num: int, size: int):
    """Returns hex string of a 32 bit number."""
    binary_str = format(num, "032b")
    shrinked_binary_str = binary_str[-size:]
    shrinked_num = int(shrinked_binary_str, 2)
    return hex(shrinked_num)

swap_halves

swap_halves(hex_num: int) -> int

Swaps the upper and lower 16 bits of a 32-bit value used for word alignment, endianess conversion into little endian.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
227
228
229
230
231
232
233
def swap_halves(self, hex_num: int) -> int:
    """Swaps the upper and lower 16 bits of a 32-bit value used for word alignment, endianess conversion into little endian."""
    hex_str = f"{hex_num:08x}"
    first_half = hex_str[:4]
    second_half = hex_str[4:]
    swapped_hex_str = second_half + first_half
    return int(swapped_hex_str, 16)

update_ca

update_ca(
    i_mem_wstrb: int, i_cfg_access: int, i_mem_addr: int
) -> int

Builds the 48-bit command/address word using write strobe, config access, and memory address fields.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def update_ca(self, i_mem_wstrb: int, i_cfg_access: int, i_mem_addr: int) -> int:
    """Builds the 48-bit command/address word using write strobe, config access, and memory address fields."""
    or_i_mem_wstrb = int(i_mem_wstrb != 0)
    not_or_i_mem_wstrb = int(not or_i_mem_wstrb)
    _ca = 0
    _ca |= not_or_i_mem_wstrb << 47
    _ca |= int(i_cfg_access) << 46
    _ca |= (or_i_mem_wstrb & int(i_cfg_access)) << 45
    _ca &= ~((1 << 45) - (1 << 16))
    _ca |= (i_mem_addr & 0xFFFFFFF8) << 13
    _ca &= ~0x7
    _ca |= i_mem_addr & 0x7
    _ca &= (1 << 48) - 1
    return _ca

wait_100ns async

wait_100ns(dut: SimHandleBase) -> None

Waits for 100ns.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
205
206
207
async def wait_100ns(self, dut: SimHandleBase) -> None:
    """Waits for 100ns."""
    await Timer(100, "ns")

wait_until_mem_ready async

wait_until_mem_ready() -> None

Continously polls for every half clock cycle to check if the memory is ready.

Source code in src/cocotbext/hyperbus/hyperbus_controller.py
239
240
241
242
243
244
async def wait_until_mem_ready(self) -> None:
    """Continously polls for every half clock cycle to check if the memory is ready."""
    while True:
        await Timer(5, "ns")
        if self.o_mem_ready:
            break

wdata_words

wdata_words() -> List[int]

Splits the 32 bit write data into bytes, with special arrangement if CA[46]=1.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
180
181
182
183
184
185
def wdata_words(self) -> List[int]:
    """Splits the 32 bit write data into bytes, with special arrangement if CA[46]=1."""
    if self.ca >> 46 & 1:
        return [(self.wdata >> 16) & 0xFF, (self.wdata >> 24) & 0xFF]

    return [(self.wdata >> (8 * i)) & 0xFF for i in range(4)]

wstrb_words

wstrb_words() -> List[int]

Splits Write strobe into bits for each byte lane.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
187
188
189
def wstrb_words(self) -> List[int]:
    """Splits Write strobe into bits for each byte lane."""
    return [(self.wstrb >> 1) & 1, self.wstrb & 1, (self.wstrb >> 3) & 1, (self.wstrb >> 2) & 1]

HyperBus_FSM

HyperBus_FSM()

State machine class for hyperbus.

Methods:

  • ca_words

    Splits the 48bit CA into 6 bytes for transmission.

  • fsm

    FSM for states IDLE, CAs, WR_LATENCY, WRITE, READ, DONE.

  • fsm_reset

    Resets the fsm to IDLE state.

  • generate_random_data

    Generate random num number of byte.

  • get_time

    Get simulation time.

  • is_rwdsvalid

    Samples the RWDS input signal every 5ns and stores it in rwds_d.

  • log

    Logs a message value with prefixed simulation time.

  • rwds_valid

    Returns True if RWDS is valid.

  • rx_data

    Returns hex string of a 32 bit number.

  • swap_halves

    Swaps the upper and lower 16 bits of a 32-bit value used for word alignment, endianess conversion into little endian.

  • update_ca

    Builds the 48-bit command/address word using write strobe, config access, and memory address fields.

  • wdata_words

    Splits the 32 bit write data into bytes, with special arrangement if CA[46]=1.

  • wstrb_words

    Splits Write strobe into bits for each byte lane.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def __init__(self) -> None:
    """Initialization of signals in testbench and dut."""
    self.i_clk = 0
    self.i_rstn = 1
    self.i_cfg_access = 0
    self.i_mem_valid = 0
    self.o_mem_ready = 0
    self.i_mem_wstrb = 0
    self.i_mem_addr = 0
    self.i_mem_wdata = 0
    self.o_mem_rdata = 0
    self.o_csn0 = 1
    self.o_csn1 = 1
    self.o_clk = 0
    self.o_clkn = 1
    self.o_dq = 0
    self.i_dq = 0
    self.o_dq_de = 0
    self.o_rwds = 0
    self.i_rwds = 0
    self.o_rwds_de = 0
    self.o_resetn = 1
    self.io_dq: Union[int, str] = 0
    self.io_rwds = 0
    self.dq_z_en = 0
    # Internal Variables
    self.state = self.IDLE
    self.ca = 0
    self.wdata = 0
    self.wstrb = 0
    self.counter = 0
    self.mem_ready = 0
    self.mem_rdata = 0
    self.rwds_d = 0
    self.bus_clk = 0

    # High-z values
    self.highimp_8 = "z"
    for _i in range(7):
        self.highimp_8 += "z"
    self.highimp_1 = "z"

ca_words

ca_words() -> List[int]

Splits the 48bit CA into 6 bytes for transmission.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
176
177
178
def ca_words(self) -> List[int]:
    """Splits the 48bit CA into 6 bytes for transmission."""
    return [(self.ca >> (8 * i)) & 0xFF for i in range(6)]

fsm async

fsm(dut: SimHandleBase) -> None

FSM for states IDLE, CAs, WR_LATENCY, WRITE, READ, DONE.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
async def fsm(self, dut: SimHandleBase) -> None:
    """FSM for states IDLE, CAs, WR_LATENCY, WRITE, READ, DONE."""
    await Timer(5, "ns")
    while True:
        if not self.i_rstn:
            self.fsm_reset()

        elif self.state == self.IDLE:
            self.mem_ready = 0
            if self.i_mem_valid and not self.mem_ready:
                self.ca = self.update_ca(self.i_mem_wstrb, self.i_cfg_access, self.i_mem_addr)
                self.wdata = self.i_mem_wdata
                self.wstrb = self.i_mem_wstrb
                self.counter = 5
                self.state = self.CAs

        elif self.state == self.CAs:
            if self.counter:
                self.counter -= 1
            elif self.ca >> 47:
                self.counter = 3
                self.state = self.READ
            elif self.ca >> 46 & 1:
                self.counter = 1
                self.state = self.WRITE
            else:
                self.counter = self.WRITE_LATENCY
                self.state = self.WR_LATENCY

        elif self.state == self.WR_LATENCY:
            if self.counter:
                self.counter -= 1
            else:
                self.counter = 3
                self.state = self.WRITE

        elif self.state == self.WRITE:

            if self.counter:
                self.counter -= 1
            else:
                self.state = self.DONE

        elif self.state == self.READ:

            if self.rwds_valid():
                if self.counter == self.RBYTE_3:
                    self.mem_rdata = (self.i_dq << 8) | (
                        self.mem_rdata & 0xFFFF00FF
                    )
                elif self.counter == self.RBYTE_2:
                    self.mem_rdata = (self.i_dq) | (self.mem_rdata & 0xFFFFFF00)
                elif self.counter == self.RBYTE_1:
                    self.mem_rdata = (self.i_dq << 24) | (
                        self.mem_rdata & 0x00FFFFFF
                    )
                elif self.counter == self.RBYTE_0:
                    self.mem_rdata = (self.i_dq << 16) | (
                        self.mem_rdata & 0xFF00FFFF
                    )
                if self.counter:
                    self.counter -= 1
                else:
                    self.state = self.DONE

        elif self.state == self.DONE:
            self.mem_ready = 1
            self.state = self.IDLE

        self.o_csn0 = self.state in [self.IDLE, self.DONE]
        self.o_resetn = self.i_rstn
        self.o_dq = (
            self.ca_words()[self.counter]
            if self.state == self.CAs
            else (
                self.wdata_words()[self.counter] if self.state == self.WRITE else 0
            )
        )
        self.o_rwds = (
            not self.wstrb_words()[self.counter] if self.state == self.WRITE else 0
        )
        self.o_dq_de = self.state in [self.CAs, self.WRITE]
        self.o_rwds_de = self.state == self.WRITE and not (self.ca >> 46 & 1)
        self.o_mem_ready = self.mem_ready
        self.o_mem_rdata = self.mem_rdata
        self.io_dq = self.o_dq if self.o_dq_de else self.highimp_8
        self.io_rwds = (self.o_rwds if self.o_rwds_de else BinaryValue(self.highimp_1))
        await Timer(10, "ns")

fsm_reset

fsm_reset() -> None

Resets the fsm to IDLE state.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
72
73
74
75
76
77
78
def fsm_reset(self) -> None:
    """Resets the fsm to IDLE state."""
    self.ca = 0
    self.state = self.IDLE
    self.mem_ready = 0
    self.mem_rdata = 0
    self.counter = 0

generate_random_data

generate_random_data(num: int) -> bytes

Generate random num number of byte.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
235
236
237
238
239
240
241
242
def generate_random_data(self, num: int) -> bytes:
    """Generate random num number of byte."""
    int_list = []
    for _ in range(num):
        # Generate a random integer within the specified range
        random_int = random.randint(0, 2**8 - 1)
        int_list.append(random_int)
    return bytes(int_list)

get_time

get_time() -> int

Get simulation time.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
197
198
199
def get_time(self) -> int:
    """Get simulation time."""
    return get_sim_time("ns")

is_rwdsvalid async

is_rwdsvalid() -> None

Samples the RWDS input signal every 5ns and stores it in rwds_d.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
191
192
193
194
195
async def is_rwdsvalid(self) -> None:
    """Samples the RWDS input signal every 5ns and stores it in rwds_d."""
    while True:
        await Timer(5, "ns")
        self.rwds_d = self.i_rwds

log

log(msg: str) -> None

Logs a message value with prefixed simulation time.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
201
202
203
def log(self, msg: str) -> None:
    """Logs a message value with prefixed simulation time."""
    cocotb.log.info(f"[{self.get_time()}]  {msg}")

rwds_valid

rwds_valid() -> int

Returns True if RWDS is valid.

Used for data sampling.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
169
170
171
172
173
174
def rwds_valid(self) -> int:
    """Returns True if RWDS is valid.

    Used for data sampling.
    """
    return self.rwds_d or self.i_rwds

rx_data

rx_data(num: int, size: int)

Returns hex string of a 32 bit number.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
205
206
207
208
209
210
def rx_data(self, num: int, size: int):
    """Returns hex string of a 32 bit number."""
    binary_str = format(num, "032b")
    shrinked_binary_str = binary_str[-size:]
    shrinked_num = int(shrinked_binary_str, 2)
    return hex(shrinked_num)

swap_halves

swap_halves(hex_num: int) -> int

Swaps the upper and lower 16 bits of a 32-bit value used for word alignment, endianess conversion into little endian.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
227
228
229
230
231
232
233
def swap_halves(self, hex_num: int) -> int:
    """Swaps the upper and lower 16 bits of a 32-bit value used for word alignment, endianess conversion into little endian."""
    hex_str = f"{hex_num:08x}"
    first_half = hex_str[:4]
    second_half = hex_str[4:]
    swapped_hex_str = second_half + first_half
    return int(swapped_hex_str, 16)

update_ca

update_ca(
    i_mem_wstrb: int, i_cfg_access: int, i_mem_addr: int
) -> int

Builds the 48-bit command/address word using write strobe, config access, and memory address fields.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def update_ca(self, i_mem_wstrb: int, i_cfg_access: int, i_mem_addr: int) -> int:
    """Builds the 48-bit command/address word using write strobe, config access, and memory address fields."""
    or_i_mem_wstrb = int(i_mem_wstrb != 0)
    not_or_i_mem_wstrb = int(not or_i_mem_wstrb)
    _ca = 0
    _ca |= not_or_i_mem_wstrb << 47
    _ca |= int(i_cfg_access) << 46
    _ca |= (or_i_mem_wstrb & int(i_cfg_access)) << 45
    _ca &= ~((1 << 45) - (1 << 16))
    _ca |= (i_mem_addr & 0xFFFFFFF8) << 13
    _ca &= ~0x7
    _ca |= i_mem_addr & 0x7
    _ca &= (1 << 48) - 1
    return _ca

wdata_words

wdata_words() -> List[int]

Splits the 32 bit write data into bytes, with special arrangement if CA[46]=1.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
180
181
182
183
184
185
def wdata_words(self) -> List[int]:
    """Splits the 32 bit write data into bytes, with special arrangement if CA[46]=1."""
    if self.ca >> 46 & 1:
        return [(self.wdata >> 16) & 0xFF, (self.wdata >> 24) & 0xFF]

    return [(self.wdata >> (8 * i)) & 0xFF for i in range(4)]

wstrb_words

wstrb_words() -> List[int]

Splits Write strobe into bits for each byte lane.

Source code in src/cocotbext/hyperbus/hbc_fsm.py
187
188
189
def wstrb_words(self) -> List[int]:
    """Splits Write strobe into bits for each byte lane."""
    return [(self.wstrb >> 1) & 1, self.wstrb & 1, (self.wstrb >> 3) & 1, (self.wstrb >> 2) & 1]