diff --git a/decoders/lpc/pd.py b/decoders/lpc/pd.py index 2a88e304..8b1dd577 100644 --- a/decoders/lpc/pd.py +++ b/decoders/lpc/pd.py @@ -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', @@ -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'}, @@ -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 @@ -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). @@ -165,35 +176,43 @@ 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 @@ -201,6 +220,10 @@ def handle_get_ct_dr(self, lad, lad_bits): 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. @@ -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 @@ -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): @@ -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): @@ -285,26 +327,51 @@ 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): @@ -312,12 +379,24 @@ def handle_get_tar2(self, lad, lad_bits): 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 @@ -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': @@ -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() +