From 14b856187d4805a83695312974c25085254f5e24 Mon Sep 17 00:00:00 2001 From: timoschneider Date: Sun, 26 Jan 2025 18:56:40 +0100 Subject: [PATCH] added i80 decoder draft --- i80/__init__.py | 22 +++++ i80/pd.py | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 233 insertions(+) create mode 100644 i80/__init__.py create mode 100644 i80/pd.py diff --git a/i80/__init__.py b/i80/__init__.py new file mode 100644 index 0000000..bf45344 --- /dev/null +++ b/i80/__init__.py @@ -0,0 +1,22 @@ +## +## Copyright (C) 2024 Timo Schneider +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +''' +This protocol decoder can decode an i80 bus. +''' + +from .pd import Decoder diff --git a/i80/pd.py b/i80/pd.py new file mode 100644 index 0000000..e61e2b8 --- /dev/null +++ b/i80/pd.py @@ -0,0 +1,211 @@ +## +## This file is part of the libsigrokdecode project. +## +## Copyright (C) 2013-2016 Uwe Hermann +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sigrokdecode as srd +from common.srdhelper import bitpack + + +class ChannelError(Exception): + pass + +NUM_CHANNELS = 32 + +class Decoder(srd.Decoder): + api_version = 3 + id = 'i80' + name = 'Intel 8080' + longname = 'Intel 8080 like bus' + desc = 'Parallel asynchronous 8/16 bit bus.' + license = 'gplv2+' + inputs = ['logic'] + outputs = ['i80'] + tags = ['Util'] + channels = ( + {'id': 'cs', 'name': 'CS', 'desc': 'Chip select line', 'idn':'dec_i80_chan_cs'}, + {'id': 'cmd', 'name': 'CMD', 'desc': 'Command line', 'idn':'dec_i80_chan_cmd'}, + {'id': 'rst', 'name': 'RST', 'desc': 'Reset line', 'idn':'dec_i80_chan_rst'}, + {'id': 'hrdy', 'name': 'HRDY', 'desc': 'Ready line', 'idn':'dec_i80_chan_hrdy'}, + {'id': 'd0', 'name': 'D0', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d0'}, + {'id': 'd1', 'name': 'D1', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d1'}, + {'id': 'd2', 'name': 'D2', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d2'}, + {'id': 'd3', 'name': 'D3', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d3'}, + {'id': 'd4', 'name': 'D4', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d4'}, + {'id': 'd5', 'name': 'D5', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d5'}, + {'id': 'd6', 'name': 'D6', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d6'}, + {'id': 'd7', 'name': 'D7', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d7'}, + ) + optional_channels = ( + {'id': 'd8', 'name': 'D8', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d8'}, + {'id': 'd9', 'name': 'D9', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d9'}, + {'id': 'd10', 'name': 'D10', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d10'}, + {'id': 'd11', 'name': 'D11', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d11'}, + {'id': 'd12', 'name': 'D12', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d12'}, + {'id': 'd13', 'name': 'D13', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d13'}, + {'id': 'd14', 'name': 'D14', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d14'}, + {'id': 'd15', 'name': 'D15', 'desc': 'Serial data line', 'idn':'dec_i80_chan_d15'}, + {'id': 'hwe', 'name': 'HWE', 'desc': 'Write enable line', 'idn':'dec_i80_chan_hwe'}, + {'id': 'hre', 'name': 'HRE', 'desc': 'Read enable line', 'idn':'dec_i80_chan_hre'}, + ) + options = ( + {'id': 'cs_polarity', 'desc': 'Chip select active state', + 'default': 'low', 'values': ('low', 'high'), 'idn':'dec_i80_opt_cs_polarity'}, + {'id': 'cmd_polarity', 'desc': 'Command active state', + 'default': 'low', 'values': ('low', 'high'), 'idn':'dec_i80_opt_cmd_polarity'}, + {'id': 'hwe_polarity', 'desc': 'Write enable active state', + 'default': 'low', 'values': ('low', 'high'), 'idn':'dec_i80_opt_hwe_polarity'}, + {'id': 'hre_polarity', 'desc': 'Read enable active state', + 'default': 'low', 'values': ('low', 'high'), 'idn':'dec_i80_opt_hre_polarity'}, + {'id': 'rst_polarity', 'desc': 'Reset polarity', + 'default': 'low', 'values': ('low', 'high'), 'idn':'dec_i80_opt_rst_polarity'}, + {'id': 'hrdy_polarity', 'desc': 'HRDY polarity', + 'default': 'high', 'values': ('low', 'high'), 'idn':'dec_i80_opt_cs_polarity'}, + {'id': 'endianness', 'desc': 'Data endianness', + 'default': 'little', 'values': ('little', 'big'), 'idn':'dec_i80_opt_endianess'}, + ) + annotations = ( + ('command', 'Command'), + ('write_data', 'Write'), + ('read_data', 'Read'), + ('reset', 'Reset'), + ('warnings', 'Warnings'), + ) + annotation_rows = ( + ('data', 'Data', (0,1,2)), + ('events', 'Events', (3,)), + ('warnings', 'Warnings', (4,)), + ) + + def __init__(self): + self.reset() + + def reset(self): + self.items = [] + self.saved_item = None + self.saved_word = None + self.ss_word = self.es_word = None + self.first = True + self.have_clock = True + self.prv_dex = 0 + self.num_item_bits = None + + def start(self): + self.out_python = self.register(srd.OUTPUT_PYTHON) + self.out_ann = self.register(srd.OUTPUT_ANN) + + def putd(self, s, e, data): + self.put(s, e, self.out_ann, data) + + + def end(self): + #the last annotation + #self.put_py(self._data_begin, self.samplenum, ['ITEM', self._data]) + #self.put_ann(self._data_begin, self.samplenum, [0, [self.format.format(self._data)]]) + ... + + def decode(self): + # Determine which (optional) channels have input data. Insist in + # a non-empty input data set. Cope with sparse connection maps. + # Store enough state to later "compress" sampled input data. + bus_width_8_bit = all(not self.has_channel(chan) for chan in range(12, 20)) + bus_width_16_bit = all(self.has_channel(chan) for chan in range(12, 20)) + + if not bus_width_8_bit and not bus_width_16_bit: + raise ChannelError('Can\'t determine bus width. Define only 8 or 16 bits!') + + self.format = "@{{:0{}X}}".format(2 if bus_width_8_bit else 4) + + have_hwe = self.has_channel(20) + hwe_active_low = self.options['hwe_polarity'] == 'low' + + have_hre = self.has_channel(21) + hre_active_low = self.options['hre_polarity'] == 'low' + + cs_active_low = self.options['cs_polarity'] == 'low' + cmd_active_low = self.options['cmd_polarity'] == 'low' + rst_active_low = self.options['rst_polarity'] == 'low' + + trigger_conds = [ + {0: 'e'}, # CS + {2: 'e'}, # RST + {20: 'r' if hwe_active_low else 'f'}, # HWE + {21: 'r' if hre_active_low else 'f'}, # HRE + ] + + # Keep processing the input stream. Assume "always zero" for + # not-connected input lines. + + is_first = True + self._last_samplenum = 0 + + while True: + + (cs, cmd, rst, hrdy, d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10 ,d11 ,d12 ,d13 ,d14 ,d15, hwe, hre) = self.wait(trigger_conds) + + operation = (cs, cmd, hwe, hre, rst, hrdy) + if bus_width_8_bit: + data = bitpack([d0, d1, d2, d3, d4, d5, d6, d7]) + elif bus_width_16_bit: + data = bitpack([d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15]) + + if is_first: + is_first = False + self._last_operation = operation + self._data_begin = 0 + self._rst_begin = 0 + self._data = data + self._data_is_cmd = operation[1] + self._data_dir = 1 + self._last_samplenum = self.samplenum + continue + + # check edge on hwe + if have_hwe and ((not self._last_operation[2] and operation[2] and hwe_active_low) or (self._last_operation[2] and not operation[2] and not hwe_active_low)): + self._data = data + self._data_is_cmd = operation[1] if cmd_active_low else not operation[1] + self._data_dir = 1 + + # check edge on hre + elif have_hre and ((not self._last_operation[3] and operation[3] and hre_active_low) or (self._last_operation[3] and not operation[3] and not hre_active_low)): + self._data = data + self._data_is_cmd = operation[1] if cmd_active_low else not operation[1] + self._data_dir = 0 + + # check cs switch + if operation[0] != self._last_operation[0]: + # cs on + if cs_active_low != operation[0]: + self._data_begin = self.samplenum + + #cs off + elif self._data_is_cmd: + #self.putd(self._data_begin, self.samplenum, [0, [self.format.format(self._data)]]) + self.putd(self._data_begin, self.samplenum, [1, [self.format.format(data)]]) + else: + #self.putd(self._data_begin, self.samplenum, [1 if self._data_dir else 2, [self.format.format(self._data)]]) + self.putd(self._data_begin, self.samplenum, [0, [self.format.format(data)]]) + + # check rst switch + if operation[4] != self._last_operation[4]: + if rst_active_low != operation[4]: + self._rst_begin = self.samplenum + else: + self.putd(self._rst_begin, self.samplenum, [3, ['Reset']]) + + self._last_operation = operation + self._last_samplenum = self.samplenum \ No newline at end of file