Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lpc: bufixes, increase speed, added TPM start, IOwrite #94

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 131 additions & 56 deletions decoders/lpc/pd.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
0b0010: 'Grant for bus master 0',
0b0011: 'Grant for bus master 1',
0b0100: 'Reserved',
0b0101: 'Reserved',
0b0101: 'TPM',
0b0110: 'Reserved',
0b0111: 'Reserved',
0b1000: 'Reserved',
Expand Down Expand Up @@ -104,6 +104,10 @@ class Decoder(srd.Decoder):
inputs = ['logic']
outputs = []
tags = ['PC']
options = (
{'id': 'sample_point', 'desc': 'Sample Point', 'default': 'raising-edge',
'values': ('raising-edge', 'falling-edge')},
)
channels = (
{'id': 'lframe', 'name': 'LFRAME#', 'desc': 'Frame'},
{'id': 'lclk', 'name': 'LCLK', 'desc': 'Clock'},
Expand Down Expand Up @@ -141,7 +145,6 @@ def __init__(self):

def reset(self):
self.state = 'IDLE'
self.oldlclk = -1
self.lad = -1
self.addr = 0
self.cur_nibble = 0
Expand All @@ -151,12 +154,20 @@ def reset(self):
self.synccount = 0
self.oldpins = None
self.ss_block = self.es_block = None
self.prev_data = []
self.first_annotation = True


def start(self):
self.out_ann = self.register(srd.OUTPUT_ANN)

def putb(self, data):
self.put(self.ss_block, self.es_block, self.out_ann, data)
def store_annotation(self, data):
self.prev_data.append(data)

def display_stored_annotation(self):
for msg in self.prev_data:
self.put(self.ss_block, self.es_block, self.out_ann, msg)
self.prev_data = []

def handle_get_start(self, lad, lad_bits, lframe):
# LAD[3:0]: START field (1 clock cycle).
Expand All @@ -165,42 +176,54 @@ def handle_get_start(self, lad, lad_bits, lframe):
# the peripherals must use. However, the host can keep LFRAME# asserted
# multiple clocks, and we output all START fields that occur, even
# though the peripherals are supposed to ignore all but the last one.
self.es_block = self.samplenum
self.putb([1, [fields['START'][lad], 'START', 'St', 'S']])

self.store_annotation([1, [fields['START'][lad], 'START', 'St', 'S']])
self.ss_block = self.samplenum

# Output a warning if LAD[3:0] changes while LFRAME# is low.
# TODO
if (self.lad != -1 and self.lad != lad):
self.putb([0, ['LAD[3:0] changed while LFRAME# was asserted']])
self.store_annotation([0, ['LAD[3:0] changed while LFRAME# was asserted']])

if (lframe == 1):
self.store_annotation([0, ['LFRAME# is asserted too early, frame might be malformed']])


# LFRAME# is asserted (low). Wait until it gets de-asserted again
# (the host is allowed to keep it asserted multiple clocks).
if lframe != 1:
return

self.start_field = self.lad

self.state = 'GET CT/DR'

def handle_get_ct_dr(self, lad, lad_bits):
# LAD[3:0]: Cycle type / direction field (1 clock cycle).
def handle_get_ct_dr(self, lad, lad_bits, lframe):

self.es_block = self.samplenum
self.display_stored_annotation()
# LFRAME# is asserted (low). Wait until it gets de-asserted again
# (the host is allowed to keep it asserted multiple clocks).
if lframe != 1:
return

self.ss_block = self.samplenum
self.cycle_type = fields['CT_DR'].get(lad, 'Reserved / unknown')

# TODO: Warning/error on invalid cycle types.
if 'Reserved' in self.cycle_type:
self.putb([0, ['Invalid cycle type (%s)' % lad_bits]])
self.store_annotation([0, ['Invalid cycle type (%s)' % lad_bits]])


self.store_annotation([2, ['Cycle type: %s' % self.cycle_type]])

self.es_block = self.samplenum
self.putb([2, ['Cycle type: %s' % self.cycle_type]])
self.ss_block = self.samplenum

self.state = 'GET ADDR'
self.addr = 0
self.cur_nibble = 0

def handle_get_addr(self, lad, lad_bits):
# LAD[3:0]: ADDR field (4/8/0 clock cycles).
if self.cur_nibble == 0:
self.es_block = self.samplenum
self.display_stored_annotation()
self.ss_block = self.samplenum

# I/O cycles: 4 ADDR clocks. Memory cycles: 8 ADDR clocks.
# DMA cycles: no ADDR clocks at all.
Expand All @@ -209,7 +232,7 @@ def handle_get_addr(self, lad, lad_bits):
elif self.cycle_type in ('Memory read', 'Memory write'):
addr_nibbles = 8 # Address is 32bits.
else:
addr_nibbles = 0 # TODO: How to handle later on?
addr_nibbles = 4 # TODO: How to handle later on? add warning

# Addresses are driven MSN-first.
offset = ((addr_nibbles - 1) - self.cur_nibble) * 4
Expand All @@ -220,27 +243,37 @@ def handle_get_addr(self, lad, lad_bits):
self.cur_nibble += 1
return

self.es_block = self.samplenum

s = 'Address: 0x%%0%dx' % addr_nibbles
self.putb([3, [s % self.addr]])
self.ss_block = self.samplenum
self.store_annotation([3, [s % self.addr]])



if self.cycle_type in ('I/O write', 'Memory write'):
self.state = 'WRITE DATA'
elif self.cycle_type in ('Memory read', 'I/O read'):
self.state = 'GET TAR'
else:
self.state = 'END_FRAME'

self.state = 'GET TAR'
self.tar_count = 0

def handle_get_tar(self, lad, lad_bits):
# LAD[3:0]: First TAR (turn-around) field (2 clock cycles).

self.es_block = self.samplenum
self.putb([4, ['TAR, cycle %d: %s' % (self.tarcount, lad_bits)]])
self.display_stored_annotation()
self.ss_block = self.samplenum

self.store_annotation([4, ['TAR1, cycle %d: %s' % (self.tarcount, lad_bits)]])


# On the first TAR clock cycle LAD[3:0] is driven to 1111 by
# either the host or peripheral. On the second clock cycle,
# the host or peripheral tri-states LAD[3:0], but its value
# should still be 1111, due to pull-ups on the LAD lines.
if lad_bits != '1111':
self.putb([0, ['TAR, cycle %d: %s (expected 1111)' % \
self.store_annotation([0, ['TAR1, cycle %d: %s (expected 1111)' % \
(self.tarcount, lad_bits)]])

if (self.tarcount != 1):
Expand All @@ -252,26 +285,35 @@ def handle_get_tar(self, lad, lad_bits):

def handle_get_sync(self, lad, lad_bits):
# LAD[3:0]: SYNC field (1-n clock cycles).
self.es_block = self.samplenum
self.display_stored_annotation()
self.ss_block = self.samplenum

self.sync_val = lad_bits
self.cycle_type = fields['SYNC'].get(lad, 'Reserved / unknown')

# TODO: Warnings if reserved value are seen?
if 'Reserved' in self.cycle_type:
self.putb([0, ['SYNC, cycle %d: %s (reserved value)' % \
(self.synccount, self.sync_val)]])

self.es_block = self.samplenum
self.putb([5, ['SYNC, cycle %d: %s' % (self.synccount, self.sync_val)]])
self.ss_block = self.samplenum
self.store_annotation([5, ['SYNC: %s' % self.sync_val]])

# TODO
if self.cycle_type in ('I/O write', 'Memory write'):
self.state = 'GET TAR2'
elif self.cycle_type in ('Memory read', 'I/O read'):
self.state = 'GET DATA'
else:
self.state = 'END_FRAME'

self.cycle_type = fields['SYNC'].get(lad, 'Reserved / unknown')
# TODO: Warnings if reserved value are seen?
if 'Reserved' in self.cycle_type:
self.store_annotation([0, ['SYNC: %s (reserved value)' % self.sync_val]])

self.cycle_count = 0
self.state = 'GET DATA'

def handle_get_data(self, lad, lad_bits):
# LAD[3:0]: DATA field (2 clock cycles).
if self.cycle_count == 0:
self.es_block = self.samplenum
self.display_stored_annotation()
self.ss_block = self.samplenum

# Data is driven LSN-first.
if (self.cycle_count == 0):
Expand All @@ -285,39 +327,76 @@ def handle_get_data(self, lad, lad_bits):
self.cycle_count += 1
return

self.es_block = self.samplenum
self.putb([6, ['DATA: 0x%02x' % self.databyte]])
self.ss_block = self.samplenum
self.store_annotation([6, ['DATA: 0x%02x' % self.databyte]])

self.cycle_count = 0
self.state = 'GET TAR2'

def handle_write_data(self, lad, lad_bits):

# LAD[3:0]: DATA field (2 clock cycles).
if self.cycle_count == 0:
self.es_block = self.samplenum
self.display_stored_annotation()
self.ss_block = self.samplenum
# Data is driven LSN-first.
if (self.cycle_count == 0):
self.databyte = lad
elif (self.cycle_count == 1):
self.databyte |= (lad << 4)
else:
raise Exception('Invalid cycle_count: %d' % self.cycle_count)

if (self.cycle_count != 1):
self.cycle_count += 1
return

self.store_annotation([6, ['DATA: 0x%02x' % self.databyte]])

self.cycle_count = 0
self.state = 'GET TAR'

def handle_get_tar2(self, lad, lad_bits):
# LAD[3:0]: Second TAR field (2 clock cycles).

self.es_block = self.samplenum
self.putb([7, ['TAR, cycle %d: %s' % (self.tarcount, lad_bits)]])
self.display_stored_annotation()
self.ss_block = self.samplenum


self.store_annotation([7, ['TAR2, cycle %d: %s' % (self.tarcount, lad_bits)]])

# On the first TAR clock cycle LAD[3:0] is driven to 1111 by
# either the host or peripheral. On the second clock cycle,
# the host or peripheral tri-states LAD[3:0], but its value
# should still be 1111, due to pull-ups on the LAD lines.
if lad_bits != '1111':
self.putb([0, ['Warning: TAR, cycle %d: %s (expected 1111)'
self.store_annotation([0, ['Warning: TAR2, cycle %d: %s (expected 1111)'
% (self.tarcount, lad_bits)]])

if (self.tarcount != 1):
self.tarcount += 1
return

self.tarcount = 0
self.state = 'IDLE'
self.state = 'END_FRAME'

def handle_end_frame(self):
self.es_block = self.samplenum
self.display_stored_annotation()
self.state = 'IDLE'
def decode(self):
conditions = [{i: 'e'} for i in range(6)]
lclk_condition = {1: 'f'} if self.options['sample_point'] == 'falling-edge' else {1: 'r'}

while True:
pins = self.wait(conditions)
if self.state == 'IDLE':
# A valid LPC cycle starts with LFRAME# being asserted (low).
self.wait({0: "l"})
elif self.state == 'GET CT/DR':
# lframe shall go back up before proceeding
self.wait({0: "h"})

pins = self.wait(lclk_condition)

# Store current pin values for the next round.
self.oldpins = pins
Expand All @@ -326,33 +405,24 @@ def decode(self):
(lframe, lclk, lad0, lad1, lad2, lad3) = pins[:6]
(lreset, ldrq, serirq, clkrun, lpme, lpcpd, lsmi) = pins[6:]

# Only look at the signals upon rising LCLK edges. The LPC clock
# is the same as the PCI clock (which is sampled at rising edges).
if not (self.oldlclk == 0 and lclk == 1):
self.oldlclk = lclk
continue


# Store LAD[3:0] bit values (one nibble) in local variables.
# Most (but not all) states need this.
if self.state != 'IDLE':
lad = (lad3 << 3) | (lad2 << 2) | (lad1 << 1) | lad0
lad_bits = '{:04b}'.format(lad)
# self.putb([0, ['LAD: %s' % lad_bits]])

lad = (lad3 << 3) | (lad2 << 2) | (lad1 << 1) | lad0
lad_bits = '{:04b}'.format(lad)

# TODO: Only memory read/write is currently supported/tested.

# State machine
if self.state == 'IDLE':
# A valid LPC cycle starts with LFRAME# being asserted (low).
if lframe != 0:
continue
self.ss_block = self.samplenum
self.state = 'GET START'
self.lad = -1
elif self.state == 'GET START':
self.handle_get_start(lad, lad_bits, lframe)
elif self.state == 'GET CT/DR':
self.handle_get_ct_dr(lad, lad_bits)
self.handle_get_ct_dr(lad, lad_bits, lframe)
elif self.state == 'GET ADDR':
self.handle_get_addr(lad, lad_bits)
elif self.state == 'GET TAR':
Expand All @@ -361,5 +431,10 @@ def decode(self):
self.handle_get_sync(lad, lad_bits)
elif self.state == 'GET DATA':
self.handle_get_data(lad, lad_bits)
elif self.state == 'WRITE DATA':
self.handle_write_data(lad, lad_bits)
elif self.state == 'GET TAR2':
self.handle_get_tar2(lad, lad_bits)
elif self.state == 'END_FRAME':
self.handle_end_frame()