simulator.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*
  2. * Xytronic LF-1600
  3. * Open Source firmware
  4. * Simulator core
  5. *
  6. * Copyright (c) 2018 Michael Buesch <m@bues.ch>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. */
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <stdint.h>
  25. #include <stdbool.h>
  26. #include <errno.h>
  27. #include <string.h>
  28. #include <signal.h>
  29. #include <sys/time.h>
  30. #include <avr/io.h>
  31. #include <avr/interrupt.h>
  32. #include <util/delay.h>
  33. #include "../timer.h"
  34. #include "../measure.h"
  35. #include "../settings.h"
  36. #include "../menu.h"
  37. #include "../measure_temp.h"
  38. #include <thread>
  39. #include <mutex>
  40. //#define IRQ_ENDIS_DEBUG
  41. //#define IRQ_HANDLER_DEBUG
  42. #define memory_barrier() __asm__ __volatile__("" : : : "memory")
  43. #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
  44. #define _stringify(x) #x
  45. #define stringify(x) _stringify(x)
  46. struct sim_context {
  47. /* Simulator context */
  48. int initialized;
  49. std::thread io_thread;
  50. volatile int io_thread_stop;
  51. bool mainloop_stats_ena;
  52. int mainloop_count;
  53. uint64_t mainloop_count_begin;
  54. double mainloops_per_sec;
  55. /* EEPROM */
  56. std::recursive_mutex eeprom_mutex;
  57. /* UART */
  58. uint8_t uart_tx_buf[4096];
  59. unsigned int uart_tx_buf_write;
  60. unsigned int uart_tx_buf_read;
  61. std::recursive_mutex uart_mutex;
  62. /* ADC */
  63. int ADC_vect_pending;
  64. int adc_conversion_running;
  65. uint64_t adc_conversion_start;
  66. uint16_t adc_values[11];
  67. std::recursive_mutex adc_mutex;
  68. /* Timer 0 */
  69. int TIMER0_COMPA_vect_pending;
  70. uint64_t timer0_prevsample;
  71. /* Interrupts */
  72. volatile int irq_suspend_request;
  73. volatile int irq_suspended;
  74. void reset()
  75. {
  76. initialized = 0;
  77. io_thread_stop = 0;
  78. mainloop_stats_ena = true;
  79. mainloop_count = 0;
  80. mainloop_count_begin = 0;
  81. mainloops_per_sec = -1.0;
  82. memset(uart_tx_buf, 0, sizeof(uart_tx_buf));
  83. uart_tx_buf_write = 0;
  84. uart_tx_buf_read = 0;
  85. ADC_vect_pending = 0;
  86. adc_conversion_running = 0;
  87. adc_conversion_start = 0;
  88. memset(adc_values, 0, sizeof(adc_values));
  89. TIMER0_COMPA_vect_pending = 0;
  90. timer0_prevsample = 0;
  91. irq_suspend_request = 0;
  92. irq_suspended = 0;
  93. }
  94. };
  95. struct sim_context sim;
  96. /* Firmware EEPROM memory */
  97. extern const uint8_t __start_eeprom;
  98. extern const uint8_t __stop_eeprom;
  99. /* Declare firmware interrupt service routines */
  100. ISR(ADC_vect);
  101. ISR(EE_READY_vect);
  102. ISR(TIMER0_COMPA_vect);
  103. #define print_enter(name) printf(">>> enter %s()\n", name)
  104. #define print_exit(name) printf("<<< exit %s()\n", name)
  105. #ifdef IRQ_HANDLER_DEBUG
  106. # define print_enter_irqhandler(name) print_enter(name)
  107. # define print_exit_irqhandler(name) print_exit(name)
  108. #else
  109. # define print_enter_irqhandler(name) do { } while (0)
  110. # define print_exit_irqhandler(name) do { } while (0)
  111. #endif
  112. #ifdef IRQ_ENDIS_DEBUG
  113. # define print_enter_irqendis(name) print_enter(name)
  114. # define print_exit_irqendis(name) print_exit(name)
  115. #else
  116. # define print_enter_irqendis(name) do { } while (0)
  117. # define print_exit_irqendis(name) do { } while (0)
  118. #endif
  119. static bool this_is_io_thread()
  120. {
  121. return std::this_thread::get_id() == sim.io_thread.get_id();
  122. }
  123. void sim_irq_disable(void)
  124. {
  125. memory_barrier();
  126. print_enter_irqendis("irq_disable");
  127. if (!this_is_io_thread()) {
  128. sim.irq_suspend_request = 1;
  129. while (!sim.irq_suspended && !sim.io_thread_stop)
  130. std::this_thread::yield();
  131. SREG &= (uint8_t)~(1u << SREG_I);
  132. }
  133. print_exit_irqendis("irq_disable");
  134. memory_barrier();
  135. }
  136. void sim_irq_enable(void)
  137. {
  138. memory_barrier();
  139. print_enter_irqendis("irq_enable");
  140. if (!this_is_io_thread()) {
  141. sim.irq_suspend_request = 0;
  142. while (sim.irq_suspended && !sim.io_thread_stop)
  143. std::this_thread::yield();
  144. SREG |= (1u << SREG_I);
  145. }
  146. print_exit_irqendis("irq_enable");
  147. memory_barrier();
  148. }
  149. uint8_t sim_irq_disable_save(void)
  150. {
  151. uint8_t sreg_flags;
  152. memory_barrier();
  153. print_enter_irqendis("irq_disable_save");
  154. sreg_flags = SREG;
  155. sim_irq_disable();
  156. print_exit_irqendis("irq_disable_save");
  157. memory_barrier();
  158. return sreg_flags;
  159. }
  160. void sim_irq_restore(uint8_t sreg_flags)
  161. {
  162. memory_barrier();
  163. print_enter_irqendis("sim_irq_restore");
  164. if (sreg_flags & (1u << SREG_I))
  165. sim_irq_enable();
  166. print_exit_irqendis("sim_irq_restore");
  167. memory_barrier();
  168. }
  169. static uint64_t systime_ms_get(void)
  170. {
  171. struct timeval tv = {};
  172. int err;
  173. uint64_t ms;
  174. err = gettimeofday(&tv, NULL);
  175. if (err)
  176. perror("gettimeofday()");
  177. ms = (uint64_t)tv.tv_sec * 1000u;
  178. ms += (uint64_t)tv.tv_usec / 1000u;
  179. return ms;
  180. }
  181. static void EEDR_read_hook(FakeIO<uint8_t> &io)
  182. {
  183. uint8_t data;
  184. ee_addr_t addr;
  185. uint16_t offset;
  186. uint8_t *ee_pointer;
  187. std::lock_guard<std::recursive_mutex> locker(sim.eeprom_mutex);
  188. if (EECR & (1 << EERE)) {
  189. addr = EEAR;
  190. ee_pointer = (uint8_t *)addr;
  191. offset = ee_pointer - &__start_eeprom;
  192. data = (&__start_eeprom)[offset];
  193. #if 0
  194. printf("sim_ee_read_hook() at 0x%X = 0x%02X\n",
  195. (unsigned int)offset, (unsigned int)data);
  196. #endif
  197. EEDR = data;
  198. EECR &= ~(1 << EERE);
  199. }
  200. }
  201. static void UCSR0A_read_hook(FakeIO<uint8_t> &io)
  202. {
  203. UCSR0A |= (1 << UDRE0);
  204. }
  205. static void UDR0_write_hook(FakeIO<uint8_t> &io, uint8_t prev_value)
  206. {
  207. std::lock_guard<std::recursive_mutex> locker(sim.uart_mutex);
  208. sim.uart_tx_buf[sim.uart_tx_buf_write] = UDR0;
  209. if (++sim.uart_tx_buf_write >= ARRAY_SIZE(sim.uart_tx_buf))
  210. sim.uart_tx_buf_write = 0;
  211. }
  212. #define run_IRQ_vect(name) do { \
  213. if (sim.name##_pending) { \
  214. print_enter_irqhandler(stringify(name)); \
  215. sim.name##_pending = 0; \
  216. name(); \
  217. print_exit_irqhandler(stringify(name)); \
  218. } \
  219. } while (0)
  220. /* Simulate timer0 hardware */
  221. static void simulate_timer0(uint64_t systime_ms)
  222. {
  223. if (TCCR0B & ((1 << CS02) | (1 << CS01) | (1 << CS00))) {
  224. uint64_t diff_ms = systime_ms - sim.timer0_prevsample;
  225. double count_per_ms = TIMERCPS / 1000.0;
  226. double call_count = diff_ms * count_per_ms;
  227. if (call_count >= 1.0) {
  228. sim.TIMER0_COMPA_vect_pending = 1;
  229. sim.timer0_prevsample = systime_ms;
  230. }
  231. }
  232. }
  233. /* Simulate ADC hardware */
  234. static void simulate_adc(uint64_t systime_ms)
  235. {
  236. uint16_t adc_value;
  237. uint32_t adc_hz, adc_ps;
  238. uint64_t conv_runtime_ms;
  239. uint8_t mux;
  240. switch (ADCSRA & ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0))) {
  241. case MEAS_PS_2:
  242. adc_ps = 2;
  243. break;
  244. case MEAS_PS_4:
  245. adc_ps = 4;
  246. break;
  247. case MEAS_PS_8:
  248. adc_ps = 8;
  249. break;
  250. case MEAS_PS_16:
  251. adc_ps = 16;
  252. break;
  253. case MEAS_PS_32:
  254. adc_ps = 32;
  255. break;
  256. case MEAS_PS_64:
  257. adc_ps = 64;
  258. break;
  259. case MEAS_PS_128:
  260. adc_ps = 128;
  261. break;
  262. default:
  263. adc_ps = 1;
  264. break;
  265. }
  266. adc_hz = F_CPU / adc_ps;
  267. conv_runtime_ms = (uint64_t)ceil((1000.0 / adc_hz) * 13);
  268. mux = (ADMUX & ((1 << MUX3) | (1 << MUX2) | (1 << MUX1) | (1 << MUX0)));
  269. {
  270. std::lock_guard<std::recursive_mutex> locker(sim.adc_mutex);
  271. switch (mux) {
  272. case MEAS_MUX_ADC0:
  273. adc_value = sim.adc_values[0];
  274. break;
  275. case MEAS_MUX_ADC1:
  276. adc_value = sim.adc_values[1];
  277. break;
  278. case MEAS_MUX_ADC2:
  279. adc_value = sim.adc_values[2];
  280. break;
  281. case MEAS_MUX_ADC3:
  282. adc_value = sim.adc_values[3];
  283. break;
  284. case MEAS_MUX_ADC4:
  285. adc_value = sim.adc_values[4];
  286. break;
  287. case MEAS_MUX_ADC5:
  288. adc_value = sim.adc_values[5];
  289. break;
  290. case MEAS_MUX_ADC6:
  291. adc_value = sim.adc_values[6];
  292. break;
  293. case MEAS_MUX_ADC7:
  294. adc_value = sim.adc_values[7];
  295. break;
  296. case MEAS_MUX_ADC8:
  297. adc_value = sim.adc_values[8];
  298. break;
  299. case MEAS_MUX_BG:
  300. adc_value = sim.adc_values[9];
  301. break;
  302. case MEAS_MUX_GND:
  303. adc_value = sim.adc_values[10];
  304. break;
  305. default:
  306. adc_value = 0;
  307. break;
  308. }
  309. }
  310. if (sim.adc_conversion_running) {
  311. if (ADCSRA & (1 << ADSC)) {
  312. if (systime_ms - sim.adc_conversion_start >= conv_runtime_ms) {
  313. sim.adc_conversion_running = 0;
  314. ADC = adc_value;
  315. if (ADCSRA & (1 << ADIE))
  316. sim.ADC_vect_pending = 1;
  317. if (!(ADCSRA & (1 << ADATE)))
  318. ADCSRA &= ~(1 << ADSC);
  319. }
  320. } else
  321. sim.adc_conversion_running = 0;
  322. } else {
  323. if (ADCSRA & (1 << ADSC)) {
  324. sim.adc_conversion_running = 1;
  325. sim.adc_conversion_start = systime_ms;
  326. }
  327. }
  328. }
  329. static void io_thread_func(void)
  330. {
  331. uint64_t systime_ms;
  332. int suspended;
  333. systime_ms = systime_ms_get();
  334. sim.timer0_prevsample = systime_ms;
  335. while (!sim.io_thread_stop) {
  336. systime_ms = systime_ms_get();
  337. /* Run the simulated hardware */
  338. simulate_timer0(systime_ms);
  339. simulate_adc(systime_ms);
  340. /* Execute interrupt handlers.
  341. * Note that in the simulator the IRQ handlers do actually
  342. * run in parallel with the main thread outside of cli-sections. */
  343. suspended = sim.irq_suspend_request;
  344. sim.irq_suspended = suspended;
  345. if (!suspended) {
  346. run_IRQ_vect(ADC_vect);
  347. run_IRQ_vect(TIMER0_COMPA_vect);
  348. }
  349. _delay_us(500);
  350. }
  351. }
  352. size_t simulator_uart_get_tx(uint8_t *buf, size_t max_bytes)
  353. {
  354. size_t count = 0;
  355. std::lock_guard<std::recursive_mutex> locker(sim.uart_mutex);
  356. while (max_bytes > 0 && sim.uart_tx_buf_read != sim.uart_tx_buf_write) {
  357. *buf = sim.uart_tx_buf[sim.uart_tx_buf_read];
  358. count++;
  359. if (++sim.uart_tx_buf_read >= ARRAY_SIZE(sim.uart_tx_buf))
  360. sim.uart_tx_buf_read = 0;
  361. max_bytes--;
  362. buf++;
  363. }
  364. return count;
  365. }
  366. bool simulator_pwm_get(int pwm_index, uint16_t *value, uint16_t *max_value)
  367. {
  368. switch (pwm_index) {
  369. case 1:
  370. *value = OCR1A;
  371. *max_value = ICR1;
  372. return true;
  373. }
  374. return false;
  375. }
  376. bool simulator_adc_set(int adc_index, uint16_t value)
  377. {
  378. std::lock_guard<std::recursive_mutex> locker(sim.adc_mutex);
  379. if (adc_index >= 0 && adc_index < (int)ARRAY_SIZE(sim.adc_values)) {
  380. sim.adc_values[adc_index] = value;
  381. return true;
  382. }
  383. return false;
  384. }
  385. #define SETTINGS_ACCESS(_field, _compare_name, _name, _value, _write) ({ \
  386. bool retval = false; \
  387. if (strcmp(_name, _compare_name) == 0) { \
  388. if (_write) { \
  389. get_settings()->_field = *(_value); \
  390. store_settings(); \
  391. } else \
  392. (*_value) = get_settings()->_field; \
  393. retval = true; \
  394. } \
  395. retval; \
  396. })
  397. bool simulator_setting_access(const char *name, int *value, bool write)
  398. {
  399. struct settings *s = get_settings();
  400. if (SETTINGS_ACCESS(temp_k[0].kp, "temp_k[0].kp", name, value, write) ||
  401. SETTINGS_ACCESS(temp_k[0].ki, "temp_k[0].ki", name, value, write) ||
  402. SETTINGS_ACCESS(temp_k[0].kd, "temp_k[0].kd", name, value, write) ||
  403. SETTINGS_ACCESS(temp_k[0].d_decay_div, "temp_k[0].d_decay_div", name, value, write) ||
  404. SETTINGS_ACCESS(temp_k[1].kp, "temp_k[1].kp", name, value, write) ||
  405. SETTINGS_ACCESS(temp_k[1].ki, "temp_k[1].ki", name, value, write) ||
  406. SETTINGS_ACCESS(temp_k[1].kd, "temp_k[1].kd", name, value, write) ||
  407. SETTINGS_ACCESS(temp_k[1].d_decay_div, "temp_k[1].d_decay_div", name, value, write) ||
  408. SETTINGS_ACCESS(temp_k[2].kp, "temp_k[2].kp", name, value, write) ||
  409. SETTINGS_ACCESS(temp_k[2].ki, "temp_k[2].ki", name, value, write) ||
  410. SETTINGS_ACCESS(temp_k[2].kd, "temp_k[2].kd", name, value, write) ||
  411. SETTINGS_ACCESS(temp_k[2].d_decay_div, "temp_k[2].d_decay_div", name, value, write)) {
  412. contrtemp_update_pid_config();
  413. return true;
  414. }
  415. if (SETTINGS_ACCESS(temp_idle_setpoint, "temp_idle_setpoint", name, value, write)) {
  416. contrtemp_set_idle_setpoint(get_settings()->temp_idle_setpoint);
  417. return true;
  418. }
  419. if (SETTINGS_ACCESS(temp_setpoint[s->temp_setpoint_active], "temp_setpoint", name, value, write) ||
  420. SETTINGS_ACCESS(temp_setpoint_active, "temp_setpoint_active", name, value, write)) {
  421. contrtemp_update_setpoint();
  422. menu_request_display_update();
  423. return true;
  424. }
  425. if (strcmp(name, "temp_adj") == 0) {
  426. if (write)
  427. meastemp_adjust_set(*value);
  428. else
  429. *value = meastemp_adjust_get();
  430. return true;
  431. }
  432. if (SETTINGS_ACCESS(serial, "serial", name, value, write))
  433. return true;
  434. return false;
  435. }
  436. void simulator_stats_ena(bool mainloop_stats_ena)
  437. {
  438. sim.mainloop_stats_ena = mainloop_stats_ena;
  439. sim.mainloop_count = 0;
  440. sim.mainloop_count_begin = systime_ms_get();
  441. sim.mainloops_per_sec = -1.0;
  442. }
  443. void main_loop_once(void);
  444. void simulator_mainloop_once(void)
  445. {
  446. uint64_t begin = 0, end = 0, runtime = 0;
  447. if (!sim.initialized)
  448. return;
  449. if (sim.mainloop_stats_ena)
  450. begin = systime_ms_get();
  451. main_loop_once();
  452. if (sim.mainloop_stats_ena) {
  453. end = systime_ms_get();
  454. runtime = end - begin;
  455. if (++sim.mainloop_count >= 100) {
  456. double loops_per_sec;
  457. uint64_t duration;
  458. duration = end - sim.mainloop_count_begin;
  459. loops_per_sec = (double)(sim.mainloop_count * 1000) / (double)duration;
  460. sim.mainloops_per_sec = loops_per_sec;
  461. sim.mainloop_count = 0;
  462. sim.mainloop_count_begin = end;
  463. }
  464. printf("Mainloop core runtime %llu ms\n%.1lf loops per second wallclock\n",
  465. (unsigned long long)runtime,
  466. sim.mainloops_per_sec);
  467. }
  468. }
  469. void simulator_exit(void)
  470. {
  471. if (!sim.initialized)
  472. return;
  473. sim.initialized = 0;
  474. printf("Exiting simulator...\n");
  475. sim.io_thread_stop = 1;
  476. sim.io_thread.join();
  477. }
  478. int program_main(void) _mainfunc;
  479. bool simulator_init(void)
  480. {
  481. if (sim.initialized)
  482. return false;
  483. sim.reset();
  484. simulator_stats_ena(true);
  485. fakeio_reset_all();
  486. /* Install I/O register hooks. */
  487. EEDR.set_read_hook(EEDR_read_hook);
  488. UCSR0A.set_read_hook(UCSR0A_read_hook);
  489. UDR0.set_write_hook(UDR0_write_hook);
  490. printf("Creating I/O-thread...\n");
  491. sim.io_thread = std::thread(io_thread_func);
  492. sim.initialized = 1;
  493. printf("Initializing firmware...\n");
  494. program_main();
  495. printf("Firmware initialized.\n");
  496. return true;
  497. }