Skip to content

Commit 028ce37

Browse files
author
ga
committed
Add an option to disable pullup simulation in avr_ioport.c with a macro
to set it in firmware. Also simulate the PUD bit for atmegax8 cores. Move the ioport definition in sim_megax8.h as they should be first after ba6dfa4.
1 parent 93b4826 commit 028ce37

9 files changed

Lines changed: 353 additions & 8 deletions

File tree

simavr/cores/sim_megax8.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ void mx8_reset(struct avr_t * avr);
4444
*/
4545
struct mcu_t {
4646
avr_t core;
47+
avr_ioport_t portb,portc,portd;
4748
avr_eeprom_t eeprom;
4849
avr_watchdog_t watchdog;
4950
avr_flash_t selfprog;
5051
avr_extint_t extint;
51-
avr_ioport_t portb,portc,portd;
5252
avr_uart_t uart;
5353
avr_acomp_t acomp;
5454
avr_adc_t adc;
@@ -79,6 +79,7 @@ const struct mcu_t SIM_CORENAME = {
7979

8080
.init = mx8_init,
8181
.reset = mx8_reset,
82+
.frequency = 1000000, // Default CKDIV8 fuse.
8283
},
8384
AVR_EEPROM_DECLARE(EE_READY_vect),
8485
#ifdef RWWSRE
@@ -93,6 +94,7 @@ const struct mcu_t SIM_CORENAME = {
9394
},
9495
.portb = {
9596
.name = 'B', .r_port = PORTB, .r_ddr = DDRB, .r_pin = PINB,
97+
.pud = AVR_IO_REGBIT(MCUCR, PUD),
9698
.pcint = {
9799
.enable = AVR_IO_REGBIT(PCICR, PCIE0),
98100
.raised = AVR_IO_REGBIT(PCIFR, PCIF0),
@@ -102,6 +104,7 @@ const struct mcu_t SIM_CORENAME = {
102104
},
103105
.portc = {
104106
.name = 'C', .r_port = PORTC, .r_ddr = DDRC, .r_pin = PINC,
107+
.pud = AVR_IO_REGBIT(MCUCR, PUD),
105108
.pcint = {
106109
.enable = AVR_IO_REGBIT(PCICR, PCIE1),
107110
.raised = AVR_IO_REGBIT(PCIFR, PCIF1),
@@ -111,6 +114,7 @@ const struct mcu_t SIM_CORENAME = {
111114
},
112115
.portd = {
113116
.name = 'D', .r_port = PORTD, .r_ddr = DDRD, .r_pin = PIND,
117+
.pud = AVR_IO_REGBIT(MCUCR, PUD),
114118
.pcint = {
115119
.enable = AVR_IO_REGBIT(PCICR, PCIE2),
116120
.raised = AVR_IO_REGBIT(PCIFR, PCIF2),

simavr/sim/avr/avr_mcu_section.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ enum {
6666
AVR_MMCU_TAG_VCD_SRAM_16,
6767
AVR_MMCU_TAG_VCD_IO_IRQ,
6868
AVR_MMCU_TAG_PORT_EXTERNAL_PULL,
69+
AVR_MMCU_TAG_PORT_NO_PULL,
6970
};
7071

7172
enum {
@@ -153,7 +154,7 @@ struct avr_mmcu_vcd_ioirq_t {
153154
}
154155

155156
#define AVR_MCU_BYTE(_tag, _val) \
156-
const uint8_t _##_tag _MMCU_ = { _tag, 1, _val }
157+
const uint8_t _##_tag[] _MMCU_ = { _tag, 1, _val }
157158

158159
/*!
159160
* This Macro allows you to specify traces for the VCD file output
@@ -332,6 +333,13 @@ struct avr_mmcu_vcd_ioirq_t {
332333
AVR_MCU_LONG(AVR_MMCU_TAG_AVCC, (_avcc));\
333334
AVR_MCU_LONG(AVR_MMCU_TAG_AREF, (_aref));
334335

336+
/*!
337+
* This tag stops the simulator from guessing the state of pins with
338+
* pull-ups enabled.
339+
*/
340+
341+
#define AVR_MCU_PORT_NO_PULL AVR_MCU_BYTE(AVR_MMCU_TAG_PORT_NO_PULL, 0)
342+
335343
/*!
336344
* Sets the MCU type and speed.
337345
*/

simavr/sim/avr_ioport.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ avr_ioport_read(
6464
avr_ioport_t * p = (avr_ioport_t *)param;
6565
uint8_t ddr = avr->data[p->r_ddr];
6666
uint8_t v = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
67+
6768
avr->data[addr] = v;
6869
avr_raise_irq(p->io.irq + IOPORT_IRQ_REG_PIN, v);
6970
D(if (avr->data[addr] != v) printf("** PIN%c(%02x) = %02x\r\n", p->name, addr, v);)
@@ -88,10 +89,15 @@ avr_ioport_update_irqs(
8889
for (int i = 0; i < 8; i++) {
8990
if (ddr & (1 << i))
9091
avr_raise_irq(p->io.irq + i, (avr->data[p->r_port] >> i) & 1);
91-
else if (p->external.pull_mask & (1 << i))
92-
avr_raise_irq(p->io.irq + i, (p->external.pull_value >> i) & 1);
93-
else if ((avr->data[p->r_port] >> i) & 1)
94-
avr_raise_irq(p->io.irq + i, 1);
92+
else if (!avr->options.no_pullups) {
93+
if (p->external.pull_mask & (1 << i))
94+
avr_raise_irq(p->io.irq + i,
95+
(p->external.pull_value >> i) & 1);
96+
else if (((avr->data[p->r_port] >> i) & 1) &&
97+
(!p->pud.reg || avr_regbit_get(avr, p->pud) == 0)) {
98+
avr_raise_irq(p->io.irq + i, 1);
99+
}
100+
}
95101
}
96102
uint8_t pin = (avr->data[p->r_pin] & ~ddr) | (avr->data[p->r_port] & ddr);
97103
pin = (pin & ~p->external.pull_mask) | p->external.pull_value;
@@ -199,6 +205,7 @@ avr_ioport_irq_notify(
199205
if (value && irq->irq != IOPORT_IRQ_PIN_ALL_IN)
200206
value = mask;
201207
new_pin = (avr->data[p->r_pin] & ~mask) | (value & mask);
208+
new_pin = (avr->data[p->r_port] & ddr) | (new_pin & ~ddr);
202209
old_value = irq->value;
203210

204211
if (output) {
@@ -224,7 +231,6 @@ avr_ioport_irq_notify(
224231
// Set the real PIN bit.
225232

226233
if (irq->irq == IOPORT_IRQ_PIN_ALL_IN) {
227-
new_pin = (avr->data[p->r_port] & ddr) | (new_pin & ~ddr);
228234
p->irqing = 1;
229235
for (int i = 0; i < 8; ++i)
230236
avr_raise_irq(p->io.irq + i, (new_pin >> i) & 1);
@@ -331,7 +337,8 @@ avr_ioport_ioctl(
331337
/*
332338
* Set the default IRQ values when pin is set as input
333339
*/
334-
if (ctl == AVR_IOCTL_IOPORT_SET_EXTERNAL(p->name)) {
340+
if (ctl == AVR_IOCTL_IOPORT_SET_EXTERNAL(p->name) &&
341+
!avr->options.no_pullups) {
335342
avr_ioport_external_t * m = (avr_ioport_external_t*)io_param;
336343
p->external.pull_mask = m->mask;
337344
p->external.pull_value = m->value;

simavr/sim/avr_ioport.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ typedef struct avr_ioport_t {
103103
avr_io_addr_t r_port;
104104
avr_io_addr_t r_ddr;
105105
avr_io_addr_t r_pin;
106+
avr_regbit_t pud; // Pull-up disable bit.
106107

107108
avr_int_vector_t pcint; // PCINT vector
108109
avr_io_addr_t r_pcint; // pcint 8 pins mask

simavr/sim/sim_avr.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ enum {
8989
#define AVR_DATA_TO_IO(v) ((v) - 32)
9090
#define AVR_IO_TO_DATA(v) ((v) + 32)
9191

92+
/**
93+
* Structure to hold simulator options.
94+
* The current set is kept in avr->options.
95+
*/
96+
97+
struct simavr_options {
98+
unsigned int no_pullups : 1; // Disable all ioport pull-up behaviours.
99+
};
100+
92101
/**
93102
* Logging macros and associated log levels.
94103
* The current log level is kept in avr->log.
@@ -234,6 +243,8 @@ typedef struct avr_t {
234243
uint32_t sleep_usec;
235244
uint64_t time_base; // for avr_get_time_stamp()
236245

246+
struct simavr_options options; // Simulation options.
247+
237248
// called at init time
238249
void (*init)(struct avr_t * avr);
239250
// called at reset time

simavr/sim/sim_elf.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ avr_load_firmware(
299299
}
300300
avr_set_command_register(avr, firmware->command_register_addr);
301301
avr_set_console_register(avr, firmware->console_register_addr);
302+
avr->options = firmware->options;
302303

303304
// rest is initialization of the VCD file
304305
if (firmware->tracecount == 0)
@@ -516,6 +517,9 @@ elf_parse_mmcu_section(
516517
case AVR_MMCU_TAG_SIMAVR_CONSOLE: {
517518
firmware->console_register_addr = src[0] | (src[1] << 8);
518519
} break;
520+
case AVR_MMCU_TAG_PORT_NO_PULL: {
521+
firmware->options.no_pullups = 1;
522+
} break;
519523
}
520524
size -= next;
521525
src += next - 2; // already incremented

simavr/sim/sim_elf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ typedef struct elf_firmware_t {
9191
char * dwarf_file; // Must be dynamically allocated.
9292
#endif
9393
char * file_name; // Internal use.
94+
struct simavr_options options;
9495
} elf_firmware_t ;
9596

9697
/* The structure *firmware must be pre-initialised to zero, then optionally

tests/atmega48_ioport.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#ifndef F_CPU
2+
#define F_CPU 8000000
3+
#endif
4+
#include <avr/io.h>
5+
#include <stdio.h>
6+
#include <avr/interrupt.h>
7+
#include <avr/sleep.h>
8+
9+
/*
10+
* This demonstrate how to use the avr_mcu_section.h file
11+
* The macro adds a section to the ELF file with useful
12+
* information for the simulator
13+
*/
14+
#include "avr_mcu_section.h"
15+
AVR_MCU(F_CPU, "atmega48");
16+
17+
/* This program is very similar to atmega168_test.c, but runs with pull-ups
18+
* disabled, both by the next macro and by setting the PUD bit in MCUCR.
19+
* Comment out one or both to check that both ways work.
20+
*/
21+
22+
AVR_MCU_PORT_NO_PULL; // Pull-ups disabled in simulator.
23+
24+
static int uart_putchar(char c, FILE *stream) {
25+
if (c == '\n')
26+
uart_putchar('\r', stream);
27+
loop_until_bit_is_set(UCSR0A, UDRE0);
28+
UDR0 = c;
29+
return 0;
30+
}
31+
32+
static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL,
33+
_FDEV_SETUP_WRITE);
34+
35+
ISR(INT0_vect)
36+
{
37+
printf("I<%02X ", PIND);
38+
}
39+
40+
ISR(PCINT0_vect)
41+
{
42+
printf("K ");
43+
}
44+
45+
ISR(PCINT2_vect)
46+
{
47+
printf("J<%02X ", PORTD);
48+
PORTD = 0;
49+
}
50+
51+
int main()
52+
{
53+
stdout = &mystdout;
54+
55+
MCUCR |= _BV(PUD); // Pull-ups disabled by "hardware".
56+
57+
/* Enable output on Port D pins 0-3 and write to them. */
58+
59+
DDRD = 0xf;
60+
PORTD = 0xa;
61+
62+
printf("P<%02X ", PIND); // Should say P<2A as caller sets bit 5.
63+
64+
/* Toggle some outputs and PORTD bits. */
65+
66+
PIND = 3; // PORTD == 0x09, pins now 0x29
67+
68+
/* Change directions. */
69+
70+
DDRD = 0x3c; // Pins now 0x9
71+
72+
/* Change output. */
73+
74+
PORTD = 0xf0;
75+
76+
/* This should say P<30 - the controlling program turns off bit 0. */
77+
78+
printf("P<%02X ", PIND);
79+
80+
/* Set-up rising edge interrupt on pin 2 (INT 0). */
81+
82+
EICRA = 3;
83+
EIMSK = 1;
84+
85+
#ifdef NOTYET
86+
/* Clear external interrupt flags, but INT0 (PD3) will
87+
* immediately reset, as the pin is low.
88+
*/
89+
90+
EIFR = 0xff;
91+
#endif
92+
/* Turn off pin 4, signal the controlling program to raise pin 2. */
93+
94+
PORTD = 0xe0;
95+
96+
/* Verify the interrupt flag is set. Result should be 3. */
97+
98+
printf("F<%02X ", EIFR);
99+
100+
sei();
101+
102+
/* This duplicates the value in the INT0 handler, but it
103+
* takes sufficient time to be sure that there is only one
104+
* interrupt. There was a bug that caused continuous interrupts
105+
* when this was first tried.
106+
*/
107+
108+
printf("P<%02X ", PIND);
109+
110+
/* TODO: Test the level-triggered interupt. It can be started
111+
* by a pin-value change or by writing to either of EICRA and EIMSK.
112+
*/
113+
114+
/* Try pin change interrupt. */
115+
116+
PCICR = (1 << PCIE2); /* Interrupt enable. */
117+
PCMSK2 = 0x0a; /* Pins 1 and 3. */
118+
DDRD = 3;
119+
PORTD = 1; /* No interrupt. */
120+
PORTD = 3; /* Interrupt. */
121+
122+
/* Allow time for second interrupt. */
123+
124+
printf("P<%02X ", PIND);
125+
126+
// Test "write 1 to clear" on PORT B.
127+
128+
DDRB = 0xff;
129+
PCICR = (1 << PCIE0); /* Interrupt enable. */
130+
PCMSK0 = 3; /* Pins 0 and 1. */
131+
cli();
132+
PORTB = 1;
133+
PCIFR = 1; /* Clear interrupt. */
134+
sei();
135+
printf("| ");
136+
cli();
137+
PORTB = 3;
138+
PCIFR = 6;
139+
sei(); /* Interrupt. */
140+
printf("| ");
141+
142+
/* Prompt for input with IOPORT_IRQ_PIN_ALL_IN. */
143+
144+
DDRB = 2;
145+
printf("P<%02X ", PIND); // Should get 98, not 99 because DDRD == 3.
146+
147+
// this quits the simulator, since interupts are off
148+
// this is a "feature" that allows running tests cases and exit
149+
cli();
150+
sleep_cpu();
151+
}

0 commit comments

Comments
 (0)