fixed data bus output buffer enable
[romdev] / fw / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <avr/io.h>
5 #include <avr/interrupt.h>
6 #include "serial.h"
7
8 /* pin assignments
9  * B[0,1]: D0,D1
10  * D[2,7]: D2-D7
11  * B2: SPI ~SS/shiftreg ~OE
12  * B3: SPI MOSI
13  * B4: SPI MISO (unused)
14  * B5: SPI SCK
15  * C0: programming mode PGM
16  * C1: RAM ~CS
17  * C2: RAM ~WE
18  * C3: RAM ~OE
19  * C5: ~SYSRST
20  */
21 #define PB_SS           0x04
22 #define PB_MOSI         0x08
23 #define PB_MISO         0x10
24 #define PB_SCK          0x20
25 #define PC_PGM          0x01
26 #define PC_CS           0x02
27 #define PC_WE           0x04
28 #define PC_OE           0x08
29 #define PC_SYSRST       0x20
30
31 static void proc_cmd(char *input);
32 static void start_prog(void);
33 static void end_prog(void);
34 static void write_ram(uint16_t addr, uint8_t val);
35 static uint8_t read_ram(uint16_t addr);
36 static void set_data_bus(unsigned char val);
37 static void release_data_bus(void);
38 static void set_addr_bus(uint16_t addr);
39 static void sys_reset(void);
40 static inline void iodelay(void);
41
42 static int echo;
43 static int progmode;
44 static uint16_t addr;
45
46 static char input[128];
47 static unsigned char inp_cidx;
48
49
50 int main(void)
51 {
52         /* SPI (SS/MOSI/SCK) are outputs */
53         DDRB = PB_SS | PB_MOSI | PB_SCK;
54         PORTB = PB_SS;  /* drive SS high when not in programming mode */
55         DDRC = 0xff;    /* control signals are all outputs */
56         PORTC = PC_CS | PC_WE | PC_OE | PC_SYSRST;
57         DDRD = 0;       /* tri-state the data bus by default (high 6 bits, low 2 are in DDRB) */
58         PORTD = 0;
59
60         /* disable all pullups */
61         MCUCR |= 1 << PUD;
62
63         /* enable SPI (used to talk to the address bus shift registers)
64          * SPI master mode, clock / 16, CPOL=0/CPHA=0 because shiftreg clocks on the
65          * rising edge, and MSB first
66          */
67         SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
68
69         /* init the serial port we use to talk to the host */
70         init_serial(38400);
71         sei();
72
73         for(;;) {
74                 if(have_input()) {
75                         int c = getchar();
76                         if(echo) {
77                                 putchar(c);
78                         }
79
80                         if(c == '\r' || c == '\n') {
81                                 input[inp_cidx] = 0;
82                                 proc_cmd(input);
83                                 inp_cidx = 0;
84                         } else if(inp_cidx < sizeof input - 1) {
85                                 input[inp_cidx++] = c;
86                         }
87                 }
88         }
89         return 0;
90 }
91
92 static void proc_cmd(char *input)
93 {
94         char *endp;
95         int data;
96
97         switch(input[0]) {
98         case 'e':
99                 echo = input[1] == '1' ? 1 : 0;
100                 printf("OK echo %s\n", echo ? "on" : "off");
101                 break;
102
103         case 'p':
104                 if(progmode) {
105                         puts("ERR already in programming mode");
106                 } else {
107                         start_prog();
108                         puts("OK programming mode");
109                 }
110                 break;
111
112         case 'b':
113                 puts("OK booting");
114                 if(!progmode) {
115                         sys_reset();
116                 } else {
117                         end_prog();
118                 }
119                 break;
120
121         case 'a':
122                 addr = strtol(input + 1, &endp, 0);
123                 printf("OK address: %x\n", (unsigned int)addr);
124                 break;
125
126         case 'w':
127                 if(!progmode) {
128                         puts("ERR not in programming mode");
129                         break;
130                 }
131                 data = strtol(input + 1, &endp, 0);
132                 write_ram(addr++, data);
133                 puts("OK");
134                 break;
135
136         case 'r':
137                 if(!progmode) {
138                         puts("ERR not in programming mode");
139                         break;
140                 }
141                 data = read_ram(addr++);
142                 printf("OK %d\n", (int)data);
143                 break;
144
145         case '?':
146                 puts("OK command help");
147                 puts(" e 0|1: turn echo on/off");
148                 puts(" p: enter programming mode");
149                 puts(" b: exit programming mode and boot up");
150                 puts(" a <addr>: set address counter (0-ffff)");
151                 puts(" w <data>: store data byte and increment address (0-255)");
152                 puts(" r: read data byte and increment address");
153                 puts(" ?: print command help");
154                 break;
155
156         default:
157                 printf("ERR unknown command: '%c'\n", input[0]);
158         }
159 }
160
161 static void start_prog(void)
162 {
163         /* hold reset low, and take over the RAM */
164         PORTC &= ~PC_SYSRST;
165         PORTC |= PC_PGM;
166
167         progmode = 1;
168 }
169
170 static void end_prog(void)
171 {
172         /* make extra sure we don't drive the data bus */
173         release_data_bus();
174
175         /* PGM low and return reset and SS high */
176         PORTB |= PB_SS;
177         PORTC &= ~PC_PGM;
178         PORTC |= PC_SYSRST;
179
180         progmode = 0;
181 }
182
183 static void write_ram(uint16_t addr, uint8_t val)
184 {
185         set_addr_bus(addr);
186         set_data_bus(val);
187         PORTC |= PC_OE;
188         PORTC &= ~(PC_WE | PC_CS);
189         iodelay();
190         PORTC |= PC_CS;
191         PORTC |= PC_WE;
192         release_data_bus();
193 }
194
195 static uint8_t read_ram(uint16_t addr)
196 {
197         uint8_t val;
198         set_addr_bus(addr);
199         PORTC |= PC_WE;
200         PORTC &= ~(PC_CS | PC_OE);
201         iodelay();
202         val = (PIND & 0xfc) | (PINB & 3);
203         PORTC |= PC_CS | PC_OE;
204         return val;
205 }
206
207 static void set_data_bus(unsigned char val)
208 {
209         /* drive the data bus */
210         DDRD = 0xff;
211         DDRB |= 3;
212         PORTB = (PORTB & 0xfc) | (val & 3);
213         PORTD = val;
214 }
215
216 static void release_data_bus(void)
217 {
218         DDRD = 0;
219         DDRB &= 0xfc;
220 }
221
222 static void set_addr_bus(uint16_t addr)
223 {
224         PORTB &= ~PB_SS;
225         SPDR = (uint8_t)addr;
226         while(!(SPSR & (1 << SPIF)));
227         SPDR = (uint8_t)(addr >> 8);
228         while(!(SPSR & (1 << SPIF)));
229
230         PORTB |= PB_SS;
231         iodelay();
232         PORTB &= ~PB_SS;        /* SS must return low because the shiftreg OE is tied to it */
233 }
234
235 static void sys_reset(void)
236 {
237         PORTC &= ~PC_SYSRST;
238         iodelay();
239         PORTC |= PC_SYSRST;
240 }
241
242 static inline void iodelay(void)
243 {
244         asm volatile("nop");
245         asm volatile("nop");
246         asm volatile("nop");
247 }