#! /usr/bin/env python3
# vim: set fileencoding=utf-8

# Copyright (c) 2018, University of Kaiserslautern
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
#    this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
#    contributors may be used to endorse or promote products derived from
#    this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Author: Éder F. Zulian

import ctypes

# A trace file is a pre-recorded file containing memory transactions. Each
# memory transaction has a timestamp that tells the simulator when it shall
# happen, a transaction type (read or write) and a memory address given in
# hexadecimal.
#
# Here is an example syntax:
#
# ```
# # Comment lines begin with #
# # [clock-cyle]: [write|read] [hex-address]
# 31:	read	0x400140
# 33:	read	0x400160
# 56:	write	0x7fff8000
# 81:	read	0x400180
# ```
#
# The timestamp corresponds to the time the request is to be issued and it is
# given in cycles of the bus master device. Example: the device is a FPGA with
# frequency 200 MHz (clock period of 5 ns). If the timestamp is 10 it means
# that the request is to be issued when time is 50 ns.
#

# The default values given as example assume the following address mapping:
#
# DIMM Characteristics:
# Byte Offset (Y): 8    [0:2]   (8-byte-wide memory module, i.e., 64-bit-wide data bus) -> 3 bit
# Cols        (C): 1K   [3:12]  (A0 - A9)   -> 10 bit
# Rows        (R): 128K [13:29] (A0 - A16)  -> 17 bit
# Bank        (B): 8    [30:32] (BA0 - BA2) -> 3  bit
#
# 3 3 3 | 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 | 1 1 1
# 2 1 0 | 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 | 2 1 0 9 8 7 6 5 4 3 | 2 1 0
# B B B | R R R R R R R R R R R R R R R R R | C C C C C C C C C C | Y Y Y
#

# Transaction type (read or write)
transaction = 'read'

# Channel information. If your address mapping does not have channel bits keep
# it equal to 1 and set the shift to the extreme left of the address.
num_ch = 1              # Number of channels
ch_shift = 34           # Shift to reach the frist bit reserved for channels in the address
ch_mask = 0x1           # Mask for all channel bits in the address

# Bank group information. If your address mapping does not have bank groups
# keep it equal to 1 and set the shift to the extreme left of the address.
num_bank_groups = 1     # Number of bank groups
bgroup_shift = 33   	# Shift to reach the frist bit reserved for bank groups in the address
bgroup_mask = 0x1   	# Mask for all bits in the address related to bank groups

# Bank information
num_banks = 8           # Number of banks
bank_shift = 30         # Shift to reach the frist bit reserved for banks in the address
bank_mask = 0x7         # Mask for all bank bits in the address

# Row information
num_rows = 128 * 1024   # Number of rows
row_shift = 13          # Shift to reach the frist bit reserved for rows in the address
row_mask = 0x1ffff      # Mask for all row bits in the address

# Column information
num_col = 1 * 1024      # Number of columns
col_shift = 3           # Shift to reach the frist bit reserved for columns in the address
col_mask = 0x3ff        # Mask for all column bits in the address

# Burst length of 8 columns. 8 columns written/read per access (in 4 full
# clock cycles of the memory bus).
burst_len = 8

# Initial clock cycle
clock_cycle = 0

# Clock cycle increment between two accesses
clock_increment = 10


def clear_bits(mask, shift, val):
    m = ctypes.c_uint64(~(mask << shift)).value
    return ctypes.c_uint64(val & m).value


def set_bits(mask, shift, val, v):
    val = clear_bits(mask, shift, val)
    return ctypes.c_uint64(val | (v << shift)).value


address = 0
for ch in range(0, num_ch):
    address = set_bits(ch_mask, ch_shift, address, ch)
    for bg in range(0, num_bank_groups):
        address = set_bits(bgroup_mask, bgroup_shift, address, bg)
        for b in range(0, num_banks):
            address = set_bits(bank_mask, bank_shift, address, b)
            for row in range(0, num_rows):
                address = set_bits(row_mask, row_shift, address, row)
                for col in range(0, num_col, burst_len):
                    address = set_bits(col_mask, col_shift, address, col)
                    print('# clock cycle: {0:d} | {1} | address: 0x{2:010X} | channel: {3} | bank group: {4} | bank: {5} | row: {6} | column: {7}'.format(clock_cycle, transaction, address, ch, bg, b, row, col))
                    print('{0:d}:\t{1}\t0x{2:010X}'.format(clock_cycle, transaction, address))
                    clock_cycle = clock_cycle + clock_increment
