blob: 81fd570a946227571dff49fed59b9c4fc1f1feba [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (C) 2017-2020 The Project X-Ray Authors.
#
# Use of this source code is governed by a ISC-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/ISC
#
# SPDX-License-Identifier: ISC
import argparse
REGISTER_LAYOUT = {
'CLKOUT1': [
('LOW_TIME', 6),
('HIGH_TIME', 6),
('OUTPUT_ENABLE', 1),
('PHASE_MUX', 3),
],
'CLKOUT2': [
('DELAY_TIME', 6),
('NO_COUNT', 1),
('EDGE', 1),
('MX', 2),
('FRAC_WF_R', 1),
('FRAC_EN', 1),
('FRAC', 3),
('RESERVED', 1),
],
'CLKOUT2_FRACTIONAL': [
('DELAY_TIME', 6),
('NO_COUNT', 1),
('EDGE', 1),
('MX', 2),
('FRAC_WF_F', 1),
('PHASE_MUX_F', 3),
('RESERVED', 2),
],
'DIVCLK': [
('LOW_TIME', 6),
('HIGH_TIME', 6),
('NO_COUNT', 1),
('EDGE', 1),
('RESERVED', 2),
],
'LOCKREG1': [
('LKTABLE', 10, 20),
('LOCKREG1_RESERVED', 6, 0),
],
'LOCKREG2': [
('LKTABLE', 10, 0),
('LKTABLE', 5, 30),
('LOCKREG2_RESERVED', 1, 0),
],
'LOCKREG3': [
('LKTABLE', 10, 10),
('LKTABLE', 5, 35),
('LOCKREG3_RESERVED', 1, 0),
],
'FILTREG1': [
('FILTREG1_RESERVED', 8, 0),
('TABLE', 1, 6),
('FILTREG1_RESERVED', 2, 8),
('TABLE', 2, 7),
('FILTREG1_RESERVED', 2, 10),
('TABLE', 1, 9),
],
'FILTREG2': [
('FILTREG2_RESERVED', 4, 0),
('TABLE', 1, 0),
('FILTREG2_RESERVED', 2, 4),
('TABLE', 2, 1),
('FILTREG2_RESERVED', 2, 6),
('TABLE', 2, 3),
('FILTREG2_RESERVED', 2, 8),
('TABLE', 1, 5),
],
'POWER_REG': [
('POWER_REG', 16),
],
}
BASE_OFFSET = 0x00
REGISTER_MAP = []
REGISTER_MAP.append(None)
REGISTER_MAP.append(None)
for idx in range(3):
REGISTER_MAP.append(None)
REGISTER_MAP.append(None)
# 0x06 - 0x15
for output in ['CLKOUT5', 'CLKOUT0', 'CLKOUT1', 'CLKOUT2', 'CLKOUT3',
'CLKOUT4', 'CLKOUT6', 'CLKFBOUT']:
if output is not None:
REGISTER_MAP.append(('CLKOUT1', output))
if output in ['CLKOUT5', 'CLKOUT6']:
# CLKOUT5 CLKOUT2 register actually controls the fractional of
# CLKOUT0.
# CLKOUT6 CLKOUT2 register actually controls the fractional of
# CLKFBOUT.
REGISTER_MAP.append(('CLKOUT2_FRACTIONAL', output))
else:
REGISTER_MAP.append(('CLKOUT2', output))
else:
REGISTER_MAP.append(None)
REGISTER_MAP.append(None)
# 0x16
REGISTER_MAP.append(('DIVCLK', 'DIVCLK'))
# 0x17
REGISTER_MAP.append(None)
# 0x18-0x1A
REGISTER_MAP.append(('LOCKREG1', 'LOCKREG1'))
REGISTER_MAP.append(('LOCKREG2', 'LOCKREG2'))
REGISTER_MAP.append(('LOCKREG3', 'LOCKREG3'))
for _ in range(0x28 - 0x1A - 1):
REGISTER_MAP.append(None)
REGISTER_MAP.append(('POWER_REG', 'POWER_REG'))
for _ in range(0x4E - 0x28 - 1):
REGISTER_MAP.append(None)
# 0x4E - 0x4F
REGISTER_MAP.append(('FILTREG1', 'FILTREG1'))
REGISTER_MAP.append(('FILTREG2', 'FILTREG2'))
for _ in range(0x20):
REGISTER_MAP.append(None)
class RegisterAddress(object):
def __init__(self, frame_offsets, bit_offset, reverse=False):
self.frame_index = 0
self.frame_offsets = frame_offsets
self.bit_offset = bit_offset
self.bits_used = set()
self.reverse = reverse
def next_bit(self, used=True):
output = '{}_{}'.format(
self.frame_offsets[self.frame_index], self.bit_offset)
if used:
self.bits_used.add(output)
self.frame_index += 1
if self.frame_index >= len(self.frame_offsets):
self.frame_index = 0
if self.reverse:
self.bit_offset -= 1
else:
self.bit_offset += 1
return output
def passthrough_non_register_segbits(seg_in):
""" Filter input segbits file and capture register base offset.
Some MMCM bit ranges are documented registers in the PLL/MMCM dynamic
reconfiguration iterface. These features will be generated in
output_registers. In order for output_registers to function, it needs
the starting bit offset of the register space, which is based off of
base_offset_register segbit definition.
Other features generated in fuzzing are passed through.
"""
base_offset_register = 'CMT_LOWER_B.MMCME2_ADV.CLKOUT5_DIVIDE[1]'
bit_offset = None
in_use = None
with open(seg_in, 'r') as f:
for l in f:
if l.startswith(base_offset_register):
parts = l.split()
assert len(parts) == 2
assert parts[0] == base_offset_register
frame_offset, bit_index = map(int, parts[1].split('_'))
assert frame_offset == 29
assert bit_index > 3
bit_offset = bit_index + 3 + 3 * 16
continue
if 'IN_USE' in l:
assert in_use is None
in_use = l.strip()
continue
parts = l.split()
feature_parts = parts[0].split('.')
if len(feature_parts) < 3:
print(l.strip())
continue
if feature_parts[2] == 'BANDWIDTH':
continue
if '[' not in feature_parts[2]:
print(l.strip())
continue
base_feature = feature_parts[2].split('[')
if base_feature[0] in [
'CLKOUT0_DIVIDE_F',
'CLKOUT1_DIVIDE',
'CLKOUT2_DIVIDE',
'CLKOUT3_DIVIDE',
'CLKOUT4_DIVIDE',
'CLKOUT5_DIVIDE',
'CLKOUT6_DIVIDE',
'DIVCLK_DIVIDE',
'CLKFBOUT_MULT_F',
'CLKOUT0_DUTY_CYCLE',
]:
# These features are MMCM registers, so ignore the base
continue
print(l.strip())
assert bit_offset is not None
assert in_use is not None
return bit_offset, in_use
def output_registers(bit_offset, in_use):
""" Output segbits for the known MMCM register space.
The first bit offset in the register space is required to generate this
output.
"""
reg = RegisterAddress(
frame_offsets=[29, 28], bit_offset=bit_offset, reverse=True)
for idx, register in enumerate(REGISTER_MAP):
if register is None:
for _ in range(16):
reg.next_bit(used=False)
continue
layout, register_name = register
layout_bits = REGISTER_LAYOUT[layout]
simple_layout = len(layout_bits[0]) == 2
if True:
bit_count = 0
if simple_layout:
for field, width in layout_bits:
for bit in range(width):
bit_count += 1
if field is None:
reg.next_bit(used=False)
continue
print(
'CMT_LOWER_B.MMCME2_ADV.{}_{}_{}[{}] {}'.format(
register_name, layout, field, bit,
reg.next_bit()))
else:
for field, width, start_bit in layout_bits:
for bit in range(width):
bit_count += 1
if field is None:
reg.next_bit(used=False)
continue
print(
'CMT_LOWER_B.MMCME2_ADV.{}[{}] {}'.format(
field, start_bit + bit, reg.next_bit()))
assert bit_count == 16
else:
for bit in range(16):
if register_name != layout or layout in ['CLKOUT1', 'CLKOUT2']:
print(
'CMT_LOWER_B.MMCME2_ADV.{}_{}[{}] {}'.format(
register_name, layout, bit, reg.next_bit()))
else:
print(
'CMT_LOWER_B.MMCME2_ADV.{}[{}] {}'.format(
register_name, bit, reg.next_bit()))
parts = in_use.split()
feature = parts[0]
bits = [p for p in parts[1:] if p not in reg.bits_used]
print('{} {}'.format(feature, ' '.join(bits)))
def main():
parser = argparse.ArgumentParser(description="")
parser.add_argument('--seg_in')
args = parser.parse_args()
bit_offset, in_use = passthrough_non_register_segbits(args.seg_in)
output_registers(bit_offset, in_use)
if __name__ == "__main__":
main()