Commit | Line | Data |
---|---|---|
9c66c9ff JM |
1 | #include <stdio.h> |
2 | #include <unistd.h> | |
3 | #include <stdlib.h> | |
4 | #include <string.h> | |
5 | #include <errno.h> | |
6 | #include <stdint.h> | |
7 | ||
8 | #include "log.h" | |
9 | #include "serial.h" | |
10 | #include "protocol.h" | |
11 | #include "devices.h" | |
12 | #include "memory.h" | |
13 | ||
14 | #define BAUD_RATE 19200 | |
e23fe653 JM |
15 | |
16 | #define STRINGIFY(n) #n | |
17 | ||
18 | #ifdef DEFPORT | |
19 | #define PORT_NAME STRINGIFY(DEFPORT) | |
20 | #else | |
9c66c9ff | 21 | #define PORT_NAME "/dev/ttyUSB0" |
e23fe653 JM |
22 | #endif |
23 | ||
24 | #ifdef _WIN32 | |
25 | static void keypress(void) | |
26 | { | |
27 | printf("Press ENTER to exit\n"); | |
28 | getchar(); | |
29 | } | |
30 | #else | |
31 | static void keypress(void) {} | |
32 | #endif | |
9c66c9ff JM |
33 | |
34 | ||
35 | void usage(const char * name) | |
36 | { | |
37 | loge("Usage: %s [-b baud] [-p port] [-i] [-v] {file.hex}\n", name); | |
38 | loge("-b baud Set baudrate (default: %d)\n", BAUD_RATE); | |
39 | loge("-p port Set serial port (default: %s)\n", PORT_NAME); | |
40 | loge("-i ID Only mode.\n"); | |
41 | loge("-v Verify after write\n"); | |
42 | } | |
43 | ||
44 | ||
45 | #define MIN(a,b) ((a)<(b)?(a):(b)) | |
46 | ||
47 | int bitmask(int value) | |
48 | { | |
49 | int out = 0; | |
50 | if (value >= (1<<1)) out |= 1; | |
51 | if (value >= (1<<2)) out |= 1<<1; | |
52 | if (value >= (1<<3)) out |= 1<<2; | |
53 | if (value >= (1<<4)) out |= 1<<3; | |
54 | if (value >= (1<<5)) out |= 1<<4; | |
55 | if (value >= (1<<6)) out |= 1<<5; | |
56 | if (value >= (1<<7)) out |= 1<<6; | |
57 | if (value >= (1<<8)) out |= 1<<7; | |
58 | if (value >= (1<<9)) out |= 1<<8; | |
59 | if (value >= (1<<10)) out |= 1<<9; | |
60 | ||
61 | return out; | |
62 | } | |
63 | /* | |
64 | * This routine writes a memory plan out to the device | |
65 | */ | |
66 | int update_mem(port_t * pt, const devid_t * dev, uint16_t maxmem, mem_t * ram, int verify) | |
67 | { | |
68 | mem_t * block = ram; | |
69 | uint8_t buff[1024]; // scratch space | |
70 | ||
71 | for (block=ram; block != NULL; block=block->next) { | |
72 | int bstart = block->start / 2; | |
73 | ||
74 | /* skip config words, we cant write them anyway */ | |
75 | if (bstart >= 0x8000) { | |
76 | logd("UpdateMem: skip config block @ %04x", bstart); | |
77 | continue; | |
78 | } | |
79 | ||
80 | ||
81 | uint8_t * p = block->bytes; | |
82 | int left = block->len; | |
83 | uint16_t addr = bstart; | |
84 | int rowlen = dev->rowsize * 2; | |
85 | logd("UpdateMem: new block %d bytes @ %04x", block->len, bstart); | |
86 | ||
87 | while (left > 0) { | |
88 | int off = 0; | |
89 | int len = rowlen; | |
90 | ||
91 | memset(buff, 255, sizeof(buff)); | |
92 | if (addr == bstart) { | |
93 | /* first row, align the start */ | |
94 | if (addr % dev->rowsize != 0) { | |
95 | addr &= ~bitmask(dev->rowsize); | |
96 | off = (bstart - addr) * 2; | |
97 | len -= off; | |
98 | logd("UpdateMem: realigning %04X to %04X", bstart, addr); | |
99 | } | |
100 | } | |
101 | ||
102 | len = MIN(len, left); | |
103 | ||
104 | logd("UpdateMem: Preparing %d bytes @ %04X", len, addr); | |
105 | ||
106 | /* partial row write, read first */ | |
107 | if (off != 0 || len < rowlen) { | |
108 | logd("UpdateMem: Read %d words @ %04x", dev->rowsize, addr); | |
109 | if (loader_readmem(pt, addr, buff, dev->rowsize)) { | |
110 | loge("UpdateMem: Aborting on failed read"); | |
111 | return 1; | |
112 | } | |
113 | print_memory(addr, buff, dev->rowsize); | |
114 | } | |
115 | ||
116 | /* update with new values */ | |
117 | memcpy(&buff[off], p, len); | |
118 | ||
119 | /* write the row */ | |
120 | logd("UpdateMem: Writing %d words @ %04x", dev->rowsize, addr); | |
121 | print_memory(addr, buff, dev->rowsize); | |
122 | if (loader_writemem(pt, addr, buff, dev->rowsize)) { | |
123 | loge("UpdateMem: Aborting on failed write"); | |
124 | return 1; | |
125 | } | |
126 | ||
127 | if (verify) { | |
128 | uint8_t again[1024]; | |
9c66c9ff JM |
129 | logd("UpdateMem: Verify %d words @ %04x", dev->rowsize, addr); |
130 | if (loader_readmem(pt, addr, again, dev->rowsize)) { | |
131 | loge("UpdateMem: Aborting on failed read"); | |
132 | return 1; | |
133 | } | |
134 | print_memory(addr, again, dev->rowsize); | |
135 | for (int i=0; i<rowlen; i++) { | |
136 | if (again[i] != buff[i]) { | |
137 | loge("UpdateMem: Verify failed on block 0x%04X", addr); | |
138 | return 1; | |
139 | } | |
140 | } | |
141 | } | |
142 | ||
143 | /* shuffle along */ | |
144 | left -= len; | |
145 | p += len; | |
146 | addr += dev->rowsize; | |
147 | } | |
148 | } | |
149 | return 0; | |
150 | } | |
151 | ||
152 | int main(int argc, char **argv) | |
153 | { | |
154 | int opt; | |
155 | char * port = NULL; | |
156 | int baud = BAUD_RATE; | |
157 | int verify = 0; | |
158 | int idonly = 0; | |
159 | ||
160 | while ((opt=getopt(argc, argv, "h?b:p:ivd"))!=-1) { | |
161 | switch (opt) { | |
162 | case 'b': | |
163 | baud = atoi(optarg); | |
164 | break; | |
165 | case 'p': | |
166 | if (port) free(port); | |
167 | port = strdup(optarg); | |
168 | break; | |
169 | case 'v': | |
170 | verify=1; | |
171 | break; | |
172 | case 'i': | |
173 | idonly=1; | |
174 | break; | |
175 | case 'd': | |
176 | debug++; | |
177 | break; | |
178 | case 'h': | |
179 | case '?': | |
180 | default: | |
181 | usage(argv[0]); | |
182 | return 1; | |
183 | } | |
184 | } | |
185 | ||
186 | if (port == NULL) port = PORT_NAME; | |
187 | ||
188 | if (!idonly && optind >= argc) { | |
189 | loge("Error: missing hexfile"); | |
e23fe653 | 190 | keypress(); |
9c66c9ff JM |
191 | return 1; |
192 | } | |
193 | ||
194 | mem_t * ram = NULL; | |
195 | ||
196 | if (!idonly) { | |
197 | FILE * fd = NULL; | |
198 | if ((fd = fopen(argv[optind], "r"))==NULL) { | |
199 | loge("Error opening %s: %s", argv[optind], strerror(errno)); | |
e23fe653 | 200 | keypress(); |
9c66c9ff JM |
201 | return 1; |
202 | } | |
203 | ram = load_ihex(fd); | |
204 | fclose(fd); | |
205 | ||
206 | logd("Memory Summary :-"); | |
207 | list_mem(ram); | |
208 | } | |
209 | ||
210 | logd("open serial port %s at %d baud", port, baud); | |
211 | port_t * pt = serial_open(port, baud); | |
e23fe653 JM |
212 | if (pt == NULL) { |
213 | keypress(); | |
214 | return 1; | |
215 | } | |
9c66c9ff JM |
216 | |
217 | uint16_t maxmem; | |
218 | uint16_t devid; | |
219 | if (loader_connect(pt, &maxmem, &devid)) { | |
e23fe653 | 220 | keypress(); |
9c66c9ff JM |
221 | return 1; |
222 | } | |
223 | ||
224 | const devid_t * dev = devid_to_info(devid); | |
225 | ||
226 | if (idonly) { | |
227 | logi("Device ID: %04X", devid); | |
228 | logi(" Free Mem: %d words available", maxmem); | |
229 | if (dev != NULL) { | |
230 | logi(" Dev Name: %s", dev->name ); | |
231 | logi(" Max Mem: %d", dev->memsize ); | |
232 | } | |
233 | ||
e23fe653 | 234 | keypress(); |
9c66c9ff JM |
235 | return 0; |
236 | } | |
237 | if (dev) logd("Device Name: %s", dev->name); | |
238 | ||
239 | /* check that the selected program will fit on this device */ | |
240 | if (makesafe_mem(&ram, maxmem)) { | |
241 | serial_close(pt); | |
e23fe653 | 242 | keypress(); |
9c66c9ff JM |
243 | return 1; |
244 | } | |
245 | logd("After re-organisation"); | |
246 | list_mem(ram); | |
247 | ||
248 | /* now write the updated memory plan to the device */ | |
249 | if (!update_mem(pt, dev, maxmem, ram, verify)) { | |
250 | if (verify) | |
251 | logi("Device Write (and Verify) Complete"); | |
252 | else | |
253 | logi("Device Write Complete"); | |
254 | } | |
255 | ||
256 | /* finished */ | |
257 | serial_close(pt); | |
258 | logd("done."); | |
e23fe653 | 259 | keypress(); |
9c66c9ff JM |
260 | return 0; |
261 | ||
262 | } |