simio: implemented GPIO simulation.

This commit is contained in:
Daniel Beer 2011-03-11 14:51:39 +13:00
parent 5bd737616d
commit 2f99793914
5 changed files with 419 additions and 2 deletions

View File

@ -64,7 +64,7 @@ mspdebug: main.o fet.o rf2500.o dis.o uif.o olimex.o ihex.o elf32.o stab.o \
reader.o vector.o output_util.o expr.o fet_error.o binfile.o \
fet_db.o usbutil.o titext.o srec.o device.o coff.o opdb.o output.o \
cmddb.o stdcmd.o prog.o flash_bsl.o list.o simio.o simio_tracer.o \
simio_timer.o simio_wdt.o simio_hwmult.o
simio_timer.o simio_wdt.o simio_hwmult.o simio_gpio.o
$(CC) $(LDFLAGS) $(PORTS_LDFLAGS) -o $@ $^ -lusb $(READLINE_LIBS)
.c.o:

View File

@ -435,6 +435,29 @@ device classes in detail.
In the list below, each device class is listed, followed by its constructor
arguments.
.IP "\fBgpio\fR"
Digital IO port simulator. This device simulates any of the digital ports
with or without interrupt capability. It has the following configuration
parameters:
.RS
.IP "\fBbase\fR \fIaddress\fR"
Set the base address for this port. Note that for ports without interrupt
capability, the resistor enable port has a special address which is
computable from the base address.
.IP "\fBirq\fR \fIvector\fR"
Enable interrupt functionality for this port by specifying an interrupt
vector number.
.IP "\fBnoirq\fR"
Disable interrupt functionality for this port.
.IP "\fBverbose\fR"
Print a state change message every time the port output changes.
.IP "\fBquiet\fR"
Don't print anything when the port state changes (the default).
.IP "\fBset\fR \fIpin\fR \fIvalue\fR"
Set the input pin state for the given pin on this port. The \fIpin\fR
parameter should be an index between 0 and 7. The \fIvalue\fR should be
either zero (for a low state) or non-zero (for a high state).
.RE
.IP "\fBhwmult\fR"
This peripheral simulates the hardware multiplier. It has no constructor or
configuration parameters, and does not provide any extended information.

View File

@ -28,12 +28,14 @@
#include "simio_timer.h"
#include "simio_wdt.h"
#include "simio_hwmult.h"
#include "simio_gpio.h"
static const struct simio_class *const class_db[] = {
&simio_tracer,
&simio_timer,
&simio_wdt,
&simio_hwmult
&simio_hwmult,
&simio_gpio
};
/* Simulator data. We keep a list of devices on the bus, and the special

368
simio_gpio.c Normal file
View File

@ -0,0 +1,368 @@
/* MSPDebug - debugging tool for MSP430 MCUs
* Copyright (C) 2009, 2010 Daniel Beer
*
* This program is free software; you can redisgibute 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 disgibuted 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
#include "simio_device.h"
#include "simio_gpio.h"
#include "expr.h"
#include "output.h"
#define REG_IN 0
#define REG_OUT 1
#define REG_DIR 2
#define REG_IFG 3
#define REG_IES 4
#define REG_IE 5
#define REG_SEL 6
#define REG_REN 7
struct gpio {
struct simio_device base;
/* Print when output changes? */
int verbose;
/* Base address */
address_t base_addr;
/* IRQ, or -1 if disabled */
int irq;
/* Congol registers */
uint8_t regs[8];
};
static struct simio_device *gpio_create(char **arg_text)
{
struct gpio *g;
g = malloc(sizeof(*g));
if (!g) {
pr_error("gpio: can't allocate memory");
return NULL;
}
memset(g, 0, sizeof(*g));
g->base.type = &simio_gpio;
g->base_addr = 0x20;
g->irq = -1;
return (struct simio_device *)g;
}
static void gpio_destroy(struct simio_device *dev)
{
struct gpio *g = (struct gpio *)dev;
free(g);
}
static void gpio_reset(struct simio_device *dev)
{
struct gpio *g = (struct gpio *)dev;
g->regs[REG_DIR] = 0;
g->regs[REG_IFG] = 0;
g->regs[REG_IE] = 0;
g->regs[REG_SEL] = 0;
g->regs[REG_REN] = 0;
}
static int config_addr(address_t *addr, char **arg_text)
{
char *text = get_arg(arg_text);
if (!text) {
printc_err("gpio: config: expected address\n");
return -1;
}
if (expr_eval(stab_default, text, addr) < 0) {
printc_err("gpio: can't parse address: %s\n", text);
return -1;
}
return 0;
}
static int config_irq(int *irq, char **arg_text)
{
char *text = get_arg(arg_text);
address_t value;
if (!text) {
printc_err("gpio: config: expected interrupt number\n");
return -1;
}
if (expr_eval(stab_default, text, &value) < 0) {
printc_err("gpio: can't parse interrupt number: %s\n", text);
return -1;
}
*irq = value;
return 0;
}
static int config_channel(struct gpio *g, char **arg_text)
{
char *which_text = get_arg(arg_text);
char *value_text = get_arg(arg_text);
address_t which;
address_t value;
uint8_t mask;
if (!(which_text && value_text)) {
printc_err("gpio: config: expected pin and value\n");
return -1;
}
if (expr_eval(stab_default, which_text, &which) < 0) {
printc_err("gpio: can't parse pin number: %s\n",
which_text);
return -1;
}
if (expr_eval(stab_default, value_text, &value) < 0) {
printc_err("gpio: can't parse pin value: %s\n",
value_text);
return -1;
}
if (which < 0 || which > 7) {
printc_err("gpio: invalid pin number: %d\n", which);
return -1;
}
mask = 1 << which;
if (g->regs[REG_IE] & mask) {
if (((g->regs[REG_IES] & mask) &&
!value && (g->regs[REG_IN] & mask)) ||
(!(g->regs[REG_IES] & mask) &&
value && !(g->regs[REG_IN] & mask)))
g->regs[REG_IFG] |= mask;
}
if (value)
g->regs[REG_IN] |= mask;
else
g->regs[REG_IN] &= ~mask;
return 0;
}
static int gpio_config(struct simio_device *dev,
const char *param, char **arg_text)
{
struct gpio *g = (struct gpio *)dev;
if (!strcasecmp(param, "base"))
return config_addr(&g->base_addr, arg_text);
if (!strcasecmp(param, "irq"))
return config_irq(&g->irq, arg_text);
if (!strcasecmp(param, "set"))
return config_channel(g, arg_text);
if (!strcasecmp(param, "noirq")) {
g->irq = -1;
return 0;
}
if (!strcasecmp(param, "verbose")) {
g->verbose = 1;
return 0;
}
if (!strcasecmp(param, "quiet")) {
g->verbose = 0;
return 0;
}
printc_err("gpio: config: unknown parameter: %s\n", param);
return -1;
}
static void print_tristate(uint8_t mask, uint8_t value)
{
int i;
for (i = 0; i < 8; i++) {
if (!(mask & 0x80))
printc("-");
else if (value & 0x80)
printc("H");
else
printc("l");
if (i == 3)
printc(" ");
value <<= 1;
mask <<= 1;
}
}
static int port_map(struct gpio *g, address_t addr)
{
int ren_addr;
if (g->irq >= 0) {
if (addr < g->base_addr)
return -1;
if (addr >= g->base_addr + 8)
return -1;
return addr - g->base_addr;
}
if (addr >= g->base_addr && addr <= g->base_addr + 2)
return addr - g->base_addr;
if (addr == g->base_addr + 3)
return REG_SEL;
ren_addr = ((g->base_addr >> 2) & 1) |
((g->base_addr >> 4) & 2) | 0x10;
if (addr == ren_addr)
return REG_REN;
return -1;
}
static int gpio_info(struct simio_device *dev)
{
struct gpio *g = (struct gpio *)dev;
printc("Base address: 0x%04x\n", g->base_addr);
printc("Input state: ");
print_tristate(~g->regs[REG_DIR] & ~g->regs[REG_SEL], g->regs[REG_IN]);
printc("\n");
printc("Output state: ");
print_tristate(g->regs[REG_DIR] & ~g->regs[REG_SEL], g->regs[REG_OUT]);
printc("\n");
printc("Direction: ");
print_tristate(~g->regs[REG_SEL], g->regs[REG_DIR]);
printc("\n");
if (g->irq >= 0) {
printc("IRQ: %d\n", g->irq);
printc("Interrupt: ");
print_tristate(g->regs[REG_IE], g->regs[REG_IFG]);
printc("\n");
printc("Interrupt edge select: ");
print_tristate(g->regs[REG_IE], g->regs[REG_IES]);
printc("\n");
printc("Interrupt enable: ");
print_tristate(0xff, g->regs[REG_IE]);
printc("\n");
}
printc("Port select: ");
print_tristate(0xff, g->regs[REG_SEL]);
printc("\n");
printc("Resistor enable: ");
print_tristate(0xff, g->regs[REG_REN]);
printc("\n");
return 0;
}
static int gpio_write_b(struct simio_device *dev,
address_t addr, uint8_t data)
{
struct gpio *g = (struct gpio *)dev;
int index = port_map(g, addr);
if (index < 0)
return 1;
if (g->verbose && index == REG_OUT) {
uint8_t delta = (g->regs[REG_OUT] ^ data) &
g->regs[REG_DIR] & ~g->regs[REG_SEL];
if (delta) {
printc("gpio: state change on %s: ", g->base.name);
print_tristate(delta, data);
printc("\n");
}
}
g->regs[index] = data;
return 1;
}
static int gpio_read_b(struct simio_device *dev,
address_t addr, uint8_t *data)
{
struct gpio *g = (struct gpio *)dev;
int index = port_map(g, addr);
if (index < 0)
return 1;
if (addr < g->base_addr || index >= 8)
return 1;
*data = g->regs[index];
return 0;
}
static int gpio_check_interrupt(struct simio_device *dev)
{
struct gpio *g = (struct gpio *)dev;
if (g->regs[REG_IFG] & g->regs[REG_IE])
return g->irq;
return -1;
}
const struct simio_class simio_gpio = {
.name = "gpio",
.help =
"This peripheral implements a digital IO port, with optional interrupt\n"
"functionality.\n"
"\n"
"Config arguments are:\n"
" base <address>\n"
" Set the peripheral base address.\n"
" irq <interrupt>\n"
" Set the interrupt vector for input pin state changes.\n"
" noirq\n"
" Disable interrupt functionality.\n"
" verbose\n"
" Print a message when output states change.\n"
" quiet\n"
" Don't print messages as output state changes.\n"
" set <pin> <0|1>\n"
" Set input pin state.\n",
.create = gpio_create,
.destroy = gpio_destroy,
.reset = gpio_reset,
.config = gpio_config,
.info = gpio_info,
.write_b = gpio_write_b,
.read_b = gpio_read_b,
.check_interrupt = gpio_check_interrupt
};

24
simio_gpio.h Normal file
View File

@ -0,0 +1,24 @@
/* MSPDebug - debugging tool for MSP430 MCUs
* Copyright (C) 2009, 2010 Daniel Beer
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef SIMIO_GPIO_H_
#define SIMIO_GPIO_H_
extern const struct simio_class simio_gpio;
#endif