bitbang: add new bitbang sub-module
authorStephan Veigl <veigl@gmx.net>
Tue, 16 Aug 2011 13:05:21 +0000 (15:05 +0200)
committerMichel Pollet <buserror@gmail.com>
Wed, 14 Sep 2011 07:17:51 +0000 (08:17 +0100)
Signed-off-by: Stephan Veigl <veigl@gmx.net>
simavr/sim/avr_bitbang.c [new file with mode: 0644]
simavr/sim/avr_bitbang.h [new file with mode: 0644]

diff --git a/simavr/sim/avr_bitbang.c b/simavr/sim/avr_bitbang.c
new file mode 100644 (file)
index 0000000..1bee186
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+       avr_bitbang.c
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+                         2011 Stephan Veigl <veig@gmx.net>
+
+       This file is part of simavr.
+
+       simavr 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 3 of the License, or
+       (at your option) any later version.
+
+       simavr 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 simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "avr_bitbang.h"
+
+#include "sim_regbit.h"
+#include "sim_core.h"
+#include "avr_ioport.h"
+
+///@todo refactor SPI to bitbang
+
+#define BITBANG_MASK   0xFFFFFFFFUL
+
+/**
+ * read (sample) data from input pin
+ *
+ * @param p            internal bitbang structure
+ */
+static void avr_bitbang_read_bit(avr_bitbang_t *p)
+{
+       avr_ioport_state_t iostate;
+       uint8_t bit = 0;
+
+       if ( !p->enabled )
+               return;
+
+       // read from HW pin
+       if ( p->p_in.port ) {
+               avr_ioctl(p->avr, AVR_IOCTL_IOPORT_GETSTATE( p->p_in.port ), &iostate);
+               bit = ( iostate.pin >> p->p_in.pin ) & 1;
+
+               if ( p->data_order ) {
+                       // data order: shift right
+                       p->data = (p->data >> 1) | ( bit << (p->buffer_size-1));
+               } else {
+                       // data order: shift left
+                       p->data = (p->data << 1) | bit;
+               }
+
+       }
+
+       // module callback
+       if ( p->callback_bit_read ) {
+               p->callback_bit_read(bit, p->callback_param);
+       }
+
+       // data sanitary
+       p->data = p->data & ~(BITBANG_MASK << p->buffer_size);
+}
+
+/**
+ * write data to output pin
+ *
+ * @param p            bitbang structure
+ */
+static void avr_bitbang_write_bit(avr_bitbang_t *p)
+{
+       uint8_t bit = 0;
+
+       if ( !p->enabled )
+               return;
+
+       if ( p->data_order ) {
+               // data order: shift right
+               bit = p->data & 1;
+       } else {
+               // data order: shift left
+               bit = (p->data >> (p->buffer_size-1)) & 1;
+       }
+
+       // output to HW pin
+       if ( p->p_out.port ) {
+               avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_out.port ), p->p_out.pin), bit);
+       }
+
+       // module callback
+       if ( p->callback_bit_write ) {
+               p->callback_bit_write(bit, p->callback_param);
+       }
+}
+
+
+/**
+ * process clock edges (both: positive and negative edges)
+ *
+ * @param p            bitbang structure
+ *
+ */
+static void avr_bitbang_clk_edge(avr_bitbang_t *p)
+{
+       uint8_t phase = (p->clk_count & 1) ^ p->clk_phase;
+       uint8_t clk = (p->clk_count & 1) ^ p->clk_pol;
+
+       if ( !p->enabled )
+               return;
+
+       // increase clock
+       p->clk_count++;
+       clk ^= 1;
+       phase ^= 1;
+
+       // generate clock output on HW pin
+       if ( p->clk_generate && p->p_clk.port ) {
+               avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), clk);
+       }
+
+       if ( phase ) {
+               // read data in
+               avr_bitbang_read_bit(p);
+
+       } else {
+               // write data out
+               avr_bitbang_write_bit(p);
+       }
+
+       if ( p->clk_count >= (p->buffer_size*2) ) {
+               // transfer finished
+               if ( p->callback_transfer_finished ) {
+                       p->data = p->callback_transfer_finished(p->data, p->callback_param);
+               }
+               p->clk_count = 0;
+       }
+}
+
+static avr_cycle_count_t avr_bitbang_clk_timer(struct avr_t * avr, avr_cycle_count_t when, void * param)
+{
+       avr_bitbang_t * p = (avr_bitbang_t *)param;
+
+       avr_bitbang_clk_edge(p);
+
+       if ( p->enabled )
+               return when + p->clk_cycles/2;
+       else
+               return 0;
+}
+
+static void avr_bitbang_clk_hook(struct avr_irq_t * irq, uint32_t value, void * param)
+{
+       avr_bitbang_t * p = (avr_bitbang_t *)param;
+       uint8_t clk = (p->clk_count & 1) ^ p->clk_pol;
+
+       // no clock change
+       if ( clk == value )
+               return;
+
+       avr_bitbang_clk_edge(p);
+}
+
+/**
+ * reset bitbang sub-module
+ *
+ * @param avr  avr attached to
+ * @param p            bitbang structure
+ */
+void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p)
+{
+       p->avr = avr;
+       p->enabled = 0;
+       p->clk_count = 0;
+       p->data = 0;
+
+       if ( p->buffer_size < 1 || p->buffer_size > 32 ) {
+               fprintf(stderr,
+                       "Error: bitbang buffer size should be between 1 and 32. set value: %d\n", p->buffer_size);
+               abort();
+       }
+
+}
+
+/**
+ * start bitbang transfer
+ *
+ * buffers should be written / cleared in advanced
+ * timers and interrupts are connected
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_start(avr_bitbang_t * p)
+{
+       p->enabled = 1;
+       p->clk_count = 0;
+
+       if ( p->clk_phase == 0 ) {
+               // write first bit
+               avr_bitbang_write_bit(p);
+       }
+
+       if ( p->clk_generate ) {
+               // master mode, generate clock -> set timer
+               avr_cycle_timer_register(p->avr, (p->clk_cycles/2), avr_bitbang_clk_timer, p);
+       } else {
+               // slave mode -> attach clock function to clock pin
+               ///@todo test
+               avr_irq_register_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p);
+       }
+
+}
+
+
+/**
+ * stop bitbang transfer
+ *
+ * timers and interrupts are disabled
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_stop(avr_bitbang_t * p)
+{
+
+       p->enabled = 0;
+       avr_cycle_timer_cancel(p->avr, avr_bitbang_clk_timer, p);
+       avr_irq_unregister_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p);
+}
diff --git a/simavr/sim/avr_bitbang.h b/simavr/sim/avr_bitbang.h
new file mode 100644 (file)
index 0000000..3d8832f
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+       avr_bitbang.h
+
+       Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
+                         2011 Stephan Veigl <veig@gmx.net>
+
+       This file is part of simavr.
+
+       simavr 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 3 of the License, or
+       (at your option) any later version.
+
+       simavr 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 simavr.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+       @defgroup avr_bitbang   Generic BitBang Module
+       @{
+
+       Generic BitBang Module of simavr AVR simulator.
+
+       @par Features / Implementation Status
+               - easy buffer access with push() / pop() functions
+               - one input and one output pin (can be the same HW pin for I2C)
+
+       @todo
+               - one input and one output pin (can be the same HW pin for I2C)
+               - one clock pin which can be configured as input or output
+                       when the clock is output, the clock signal is generated with a
+                       configured frequency (master / slave mode)
+               - 2x 32-bit buffers (input / output) (allows start, stop bits for UART, etc.)
+               - on each read / write a callback is executed to notify the master module
+
+*/
+
+
+#ifndef AVR_BITBANG_H_
+#define AVR_BITBANG_H_
+
+#include "sim_avr.h"
+#include "avr_ioport.h"
+
+
+
+
+/// SPI Module initialization and state structure
+typedef struct avr_bitbang_t {
+       avr_t *                         avr;            ///< avr we are attached to
+
+       uint8_t enabled;                ///< bit-bang enabled flag
+       uint8_t clk_generate;   ///< generate clock and write to clock pin (if available) -> master / slave mode
+       uint8_t clk_pol;                ///< clock polarity, base (inactive) value of clock
+       uint8_t clk_phase;              ///< clock phase / data sampling edge
+                                                       ///             - 0: data are sampled at first clock edge
+                                                       ///             - 1: data are sampled at second clock edge
+       uint32_t clk_cycles;    ///< cycles per clock period - must be multiple of 2! (used if clk_generate is enabled)
+       uint8_t data_order;             ///< data order / shift
+                                                       ///             - 0: shift left
+                                                       ///             - 1: shift right
+
+       uint8_t buffer_size;    ///< size of buffer in bits (1...32)
+
+       void *callback_param;   /// anonymous parameter for callback functions
+       void (*callback_bit_read)(uint8_t bit, void *param);    ///< callback function to notify about bit read
+       void (*callback_bit_write)(uint8_t bit, void *param);   ///< callback function to notify about bit write
+       uint32_t (*callback_transfer_finished)(uint32_t data, void *param);     ///< callback function to notify about a complete transfer
+                                                                                                                                                       ///             (read received data and write new output data)
+
+       avr_iopin_t     p_clk;          ///< clock pin (optional)
+       avr_iopin_t     p_in;           ///< data in pin
+       avr_iopin_t     p_out;          ///< data out pin
+
+// private data
+       uint32_t data;                  ///< data buffer
+                                                       ///             - latest received bit the is lowest / most right one, bit number: 0
+                                                       ///             - next bit to be written is the highest one, bit number: (buffer_size-1)
+       int8_t          clk_count;      ///< internal clock edge count
+} avr_bitbang_t;
+
+/**
+ * reset bitbang sub-module
+ *
+ * @param avr  avr attached to
+ * @param p            bitbang structure
+ */
+void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p);
+
+/**
+ * start bitbang transfer
+ *
+ * buffers should be written / cleared in advanced
+ * timers and interrupts are connected
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_start(avr_bitbang_t * p);
+
+
+/**
+ * stop bitbang transfer
+ *
+ * timers and interrupts are disabled
+ *
+ * @param p                    bitbang structure
+ */
+void avr_bitbang_stop(avr_bitbang_t * p);
+
+
+#endif /* AVR_BITBANG_H_ */
+/// @} end of avr_bitbang group