Compare commits

...

133 Commits

Author SHA1 Message Date
NIIBE Yutaka
5a3a3e98d4 Version 2.2.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-26 09:16:49 +09:00
NIIBE Yutaka
7ad2c9030a One more change for Cortex-M3/M4 asm for shorter result.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-26 09:15:18 +09:00
NIIBE Yutaka
a70b1acbf6 Change asm for Cortex-M0/3/4.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-25 12:52:26 +09:00
NIIBE Yutaka
fd8bb46b8b Fix for Cortex-M0.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-25 10:35:29 +09:00
NIIBE Yutaka
95fe257dc0 cortex-m: Fix chx_handle_intr.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-19 15:56:43 +09:00
NIIBE Yutaka
2fb3c1c503 GNU/Linux: Use getrandom for ADC driver.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 16:24:22 +09:00
NIIBE Yutaka
f84f6c1cac GNU/Linux: Add start-up message for USB driver.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 10:29:08 +09:00
NIIBE Yutaka
68b78a0ade GNU/Linux: Fix AckBtn driver, again.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 10:12:08 +09:00
NIIBE Yutaka
8c48b0d7d3 GNU/Linux: Fix AckBtn driver.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 09:53:49 +09:00
NIIBE Yutaka
b3c35aebdd Add new driver for AckBtn for GNU/Linux emulation.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-17 09:47:19 +09:00
NIIBE Yutaka
c0f3567ed6 Version 2.1.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-12 09:19:08 +09:00
NIIBE Yutaka
27791641aa GNU/Linux: Fix the example.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-10 14:49:19 +09:00
NIIBE Yutaka
eaa47d5059 Update copyright notices.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-10 10:44:37 +09:00
NIIBE Yutaka
b491d815e5 GNU/Linux: Fix the example to cancel the input on timeout.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-10 10:19:06 +09:00
NIIBE Yutaka
c1433f1520 GNU/Linux: Application can exit by SIGINT or SIGTERM.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-10 09:33:53 +09:00
NIIBE Yutaka
bd66c51e35 Fix chx_recv_irq.
Fixes-commit: d2df891ba5
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-10 09:22:37 +09:00
NIIBE Yutaka
bd8f39f3c9 GNU/Linux: Add/fix comments.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-10 09:05:16 +09:00
NIIBE Yutaka
88909bab49 GNU/Linux: Make sure thread struct is cleared.
Also, added a comment for makecontext.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-09 16:50:48 +09:00
NIIBE Yutaka
66f08d87e4 Fix example for GNU/Linux emulation.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-09 16:38:13 +09:00
NIIBE Yutaka
7b7335bc5d Fix GNU/Linux emulation.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-09 16:36:40 +09:00
NIIBE Yutaka
af3ef1f93d Add comments to show access to RUNNING.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-09 13:33:51 +09:00
NIIBE Yutaka
d2df891ba5 Fix chx_recv_irq.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-08 17:33:24 +09:00
NIIBE Yutaka
cfcdeebb78 More fixes for Cortex-M0/Cortex-M3/Cortex-M4 implementations.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-08 12:22:16 +09:00
NIIBE Yutaka
2832104263 Rewrite the ChangeLog entry.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-05 15:30:56 +09:00
NIIBE Yutaka
b0986cdb09 Fix preemption.
Consider the sequence:

   chx_handle_intr -> chx_handle_intr -> preempt

We can't use R0 passing as an argument to preempt.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-05 11:26:20 +09:00
NIIBE Yutaka
c3f00e1c69 cortex-m: Multiple interrupts handling may occur on Cortex-M3 too.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-05 10:54:41 +09:00
NIIBE Yutaka
6d46ea2a4c cortex-m: Don't share return path between PREEMPT and SVC.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-05 10:49:09 +09:00
NIIBE Yutaka
7035103a49 Fix the previous commit.
Handle the case when multiple interrupts are active simultaneously.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-05 09:38:38 +09:00
NIIBE Yutaka
8b6c1ebd24 Fixes for FSM-55.
I realized that tail-chaining doesn't work with STM32F0.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-02-04 15:32:23 +09:00
NIIBE Yutaka
34e7673871 Version 2.0.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2020-06-26 10:03:26 +09:00
NIIBE Yutaka
0e5342b54f doc: Update doc/chopstx-api.texi.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2020-06-24 10:54:43 +09:00
NIIBE Yutaka
afd1339c58 Add chopstx_critical.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2020-06-23 13:29:14 +09:00
NIIBE Yutaka
86d805620c Revert "Fix EP0 receiving more packets."
This reverts commit 3507027e98.

--

Thanks to Jeremy Drake to catch this questionable change.

While the value LEN is used for drivers for other machines to prepare
receiving buffer in hardware USB core, it is not used in STM32F103
which has dedicated hardware memory in the USB core.
2020-01-06 09:58:10 +09:00
NIIBE Yutaka
22fc473eb5 Revert "Add a ChangeLog entry for USB fix."
This reverts commit fe451d6d8a.
2020-01-06 09:57:50 +09:00
NIIBE Yutaka
22a7d6c998 Fix USB driver of GD32VF1 and example-lcd.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:54:40 +09:00
NIIBE Yutaka
99023a1126 Update example-lcd.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:49:35 +09:00
NIIBE Yutaka
76f962fbdd Fix SPI driver.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:47:26 +09:00
NIIBE Yutaka
228d1d06ce Add USB driver for GD32VF103.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:42:22 +09:00
NIIBE Yutaka
167741bdc8 chopstx-gnu-linux.c: cosmetic change.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:37:33 +09:00
NIIBE Yutaka
334240bfe3 board/board-longan-nano.h: Modify Port B config.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:34:29 +09:00
NIIBE Yutaka
a006cb7d0a Add ChangeLog entry.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:26:29 +09:00
NIIBE Yutaka
4f7da4812e chopstx_poll: More change.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:24:51 +09:00
NIIBE Yutaka
50a5951422 Fix chopstx_poll for condition variables, check after woken up.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-27 09:18:11 +09:00
NIIBE Yutaka
8b4ecad359 example-cdc,etc.: Bug fix of examples.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-20 14:40:45 +09:00
NIIBE Yutaka
fe451d6d8a Add a ChangeLog entry for USB fix.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-20 08:31:27 +09:00
NIIBE Yutaka
3507027e98 Fix EP0 receiving more packets.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-20 08:30:49 +09:00
NIIBE Yutaka
746388331c Fix GNU/Linux emulation about termination of a thread.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-04 13:47:29 +09:00
NIIBE Yutaka
6ce92cc0e8 Add rules for RISC-V.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:47:55 +09:00
NIIBE Yutaka
6e1c791b04 Add example-lcd for Longan nano board.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:41:17 +09:00
NIIBE Yutaka
1dbd9811c2 Factoring of USART driver for GD32VF103. 2019-12-03 13:38:47 +09:00
NIIBE Yutaka
2de23d5fd6 Add missing spi.h.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:33:40 +09:00
NIIBE Yutaka
07d1911c2b Add Longan Nano support.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:30:42 +09:00
NIIBE Yutaka
214066fd82 Add RISC-V 32 IMAC support.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:27:16 +09:00
NIIBE Yutaka
51f2ca841f Add SPI driver.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:18:49 +09:00
NIIBE Yutaka
1978ca25b6 Merge the change in RISC-V branch.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:15:56 +09:00
NIIBE Yutaka
913266d6e4 Include arch specific header and implementation by ARCH.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-12-03 13:09:25 +09:00
NIIBE Yutaka
de301bf025 Merge the change for GNU/Linux from riscv branch.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-25 09:53:50 +09:00
NIIBE Yutaka
823ebe222c Rename to preempted_context_switch (GNU/Linux).
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-22 08:55:01 +09:00
NIIBE Yutaka
2841efd9e5 Add an idea of chopstx_critical.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-22 08:55:01 +09:00
NIIBE Yutaka
b17834876f Fix the previous commit.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-21 15:04:40 +09:00
NIIBE Yutaka
86c21fbf5c Common chx_sched and arch specific voluntary_context_switch.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-21 12:37:02 +09:00
NIIBE Yutaka
bbb952429a Fix the previous change.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-21 11:30:44 +09:00
NIIBE Yutaka
cffc8bf96c Removal of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-21 10:35:14 +09:00
NIIBE Yutaka
89523f22bf more clean up.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-21 08:01:25 +09:00
NIIBE Yutaka
0e5994506a Version 1.17.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-20 11:32:27 +09:00
NIIBE Yutaka
bdbc84ba18 chx_running for GNU/Linux port.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-20 11:24:21 +09:00
NIIBE Yutaka
c73258138c Use chx_running function.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-19 08:40:38 +09:00
NIIBE Yutaka
2180ed24be Rename internal functions to express meaning well.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-18 13:04:12 +09:00
NIIBE Yutaka
b70de1b98d Change chx_ready_pop implementation.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-18 11:55:22 +09:00
NIIBE Yutaka
355482550b New: ticks_to_usec.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-18 08:28:05 +09:00
NIIBE Yutaka
858a9f5d01 Have a entry-gnu-linux.c.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-18 08:25:56 +09:00
NIIBE Yutaka
c7b83fd51c Move CHOPSTX_THREAD_SIZE, it's core specific.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-11-18 08:13:33 +09:00
Jeremy Drake
8e55209f33 Fix USB driver. 2019-10-07 16:07:31 +09:00
NIIBE Yutaka
4bde2ae1fc Fix USB drivers.
Thanks to Jeremy Drake for the report.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2019-09-04 08:57:47 +09:00
NIIBE Yutaka
d4ba52b0d1 Version 1.16. 2019-05-22 14:22:29 +09:00
NIIBE Yutaka
b483dc460d Add ChangLog entry. 2019-05-22 08:25:10 +09:00
Evangelos Rigas
9ff47d75b5 Add support for Gnukey-DS. 2019-05-22 08:20:36 +09:00
NIIBE Yutaka
d66481d67c Fix Nucleo-32 STM32L432 board. 2019-05-20 15:40:19 +09:00
NIIBE Yutaka
b539f27475 Fix USART driver. 2019-05-20 14:26:15 +09:00
NIIBE Yutaka
128cd508b6 examples: Fixes for new USB driver which does not use SYS. 2019-05-16 11:43:08 +09:00
NIIBE Yutaka
4f46af7557 Version 1.15. 2019-05-14 10:31:24 +09:00
NIIBE Yutaka
0de43691ab Changes for USB driver. 2019-05-13 09:37:18 +09:00
NIIBE Yutaka
79305c3de4 Allow calling chopstx_claim_irq when ready (disabled). 2019-05-10 12:05:35 +09:00
NIIBE Yutaka
c1ea549995 usb: FREE_STANDING should come with USE_SYS. 2019-05-10 10:17:48 +09:00
NIIBE Yutaka
fee2cae8c4 SYS 4.0. 2019-05-10 09:59:57 +09:00
NIIBE Yutaka
3317fb39ab Add mcu/ABOUT-USB. 2019-05-10 09:37:20 +09:00
NIIBE Yutaka
de4ab0d3c9 Fix usb driver for STM32L4. 2019-05-08 12:23:53 +09:00
NIIBE Yutaka
be43aa3051 usb driver: Only board specific function of cable config is in SYS. 2019-05-08 12:07:28 +09:00
NIIBE Yutaka
e7e6f5b184 usart: move to common code. 2019-04-26 10:21:29 +09:00
NIIBE Yutaka
74e52fd7f9 usart: usart_block_sendrecv: Fix receive when no data to send. 2019-04-25 18:36:24 +09:00
NIIBE Yutaka
fe1ca5f055 usart: stm32l4 change. 2019-04-25 17:04:13 +09:00
NIIBE Yutaka
3199ac7aae Better API for baud setting for smartcard communication. 2019-04-25 11:08:46 +09:00
NIIBE Yutaka
d22ffb2d07 Fix USART driver. 2019-04-24 20:22:57 +09:00
NIIBE Yutaka
c818ec89a4 Add EXTI for STM32L. 2019-04-24 11:42:40 +09:00
NIIBE Yutaka
2d2d544c5d Add ChangLog entries, update NEWS and README.
I should have called version 1.10 as 2.0, becase it introduced API
change.
2019-04-24 09:57:51 +09:00
NIIBE Yutaka
97811f2e1c Add SYSCFG to mcu/stm32l.h. 2019-04-24 09:37:55 +09:00
NIIBE Yutaka
2db324e93d doc: Add a memo. 2019-04-24 09:35:29 +09:00
NIIBE Yutaka
886343d40d Board: Nucleo L432: Decide assignment of pin. 2019-04-22 15:40:00 +09:00
NIIBE Yutaka
f6c29ab274 Implement usart_block_sendrecv for STM32L432. Not tested. 2019-04-19 17:04:28 +09:00
NIIBE Yutaka
8afabfa301 Fix entry.c for STM32L4. Now, USB works. 2019-04-18 19:14:08 +09:00
NIIBE Yutaka
1d2aacd0a4 stm32l4: Enable PWR module and USV-bit in CR2. 2019-04-18 19:13:23 +09:00
NIIBE Yutaka
359082f80a Testing USB on STM32L4. 2019-04-18 17:12:41 +09:00
NIIBE Yutaka
1f159888a0 doc: Add memorandom chapter. 2019-04-18 10:25:19 +09:00
NIIBE Yutaka
f37d83e55d New: mcu/usb-stm32l.c (not yet tested). 2019-04-17 16:13:58 +09:00
NIIBE Yutaka
b20f66b5e4 Factor out usb-st-common.c. 2019-04-17 15:20:31 +09:00
NIIBE Yutaka
5d344acad9 usb driver: I/O access style fix. 2019-04-17 15:07:12 +09:00
NIIBE Yutaka
06eef36868 usart: Fix smartcard communication. Only ignoring echo back. 2019-04-15 12:06:57 +09:00
NIIBE Yutaka
ca06df793a Add example-usart, which works well with ST Nucleo L432. 2019-04-12 20:20:16 +09:00
NIIBE Yutaka
92de60e5f2 Fix struct usart member mistake. Add example-usart. 2019-04-12 20:05:35 +09:00
NIIBE Yutaka
61c0edcc96 Add USART driver for STM32L. 2019-04-12 17:28:41 +09:00
NIIBE Yutaka
7b129cd50f Factor out USART routines. 2019-04-12 17:08:51 +09:00
NIIBE Yutaka
f237314ebf Rename _PHR_ (as peripheral) to specific bus (AHB, APB, etc.). 2019-04-12 10:45:12 +09:00
NIIBE Yutaka
52efc84f5c Now, example-led works fine with -mcortex-m4. 2019-04-12 10:30:53 +09:00
NIIBE Yutaka
5a326eee54 Example-led works fine with -mcpu=cortex-m3. 2019-04-11 17:11:47 +09:00
NIIBE Yutaka
054950bc9a Try STM Nucleo L432 LED. 2019-04-11 15:36:51 +09:00
NIIBE Yutaka
e5e46b5de5 Add mcu/*stm32l4. 2019-04-11 15:34:57 +09:00
NIIBE Yutaka
8b9d2c007a Add comment of usb_cable_config. 2019-04-11 13:48:26 +09:00
NIIBE Yutaka
5a6910a45b Adding STM32L432 support for USART (not yet USB, ADC, Flash, etc.). 2019-04-11 11:08:17 +09:00
NIIBE Yutaka
f8880aafec Coding style fix for SYST registers. 2019-04-11 09:08:33 +09:00
NIIBE Yutaka
681a0055e4 Rename clk_gpio_init-stm32f.c. 2019-04-10 16:17:19 +09:00
NIIBE Yutaka
69a7960876 Start experiment with STM32L432. 2019-04-10 12:39:07 +09:00
NIIBE Yutaka
7f77e5a13d Use STM32F10X_HD. 2019-04-10 12:38:45 +09:00
NIIBE Yutaka
bf585aba18 Use 9600 bps for BSCARD. 2019-04-09 13:35:59 +09:00
NIIBE Yutaka
bf7afa7348 usart: Support busy-wait in usart_block_sendrecv.
chopstx_poll is heavy.
2019-04-09 10:43:53 +09:00
NIIBE Yutaka
7f4eae6c56 Modify the loop of usart_block_sendrecv. 2019-04-09 08:51:23 +09:00
NIIBE Yutaka
8c045a6b8d usart: New API for block send-recv. 2019-04-08 11:49:13 +09:00
NIIBE Yutaka
ac026cc501 St Nucleo 32: Enable AFIO clock. 2019-04-02 13:39:29 +09:00
NIIBE Yutaka
8d7106d992 Change internal function name of USART driver. 2019-04-01 21:18:03 +09:00
NIIBE Yutaka
2c0b1eee03 Fix gaurd time for 1 etu. 2019-04-01 15:33:07 +09:00
NIIBE Yutaka
83817af2d7 Enhancement for smartcard communication. 2019-03-30 19:27:53 +09:00
NIIBE Yutaka
339da2901c Fix typo. 2019-03-29 16:08:30 +09:00
NIIBE Yutaka
06130d071b Add read with timeout for USART driver. 2019-03-29 11:53:41 +09:00
NIIBE Yutaka
02ca3a6cd5 Fix typo in copyright notice. 2019-03-26 18:18:39 +09:00
118 changed files with 13439 additions and 2323 deletions

46
AUTHORS
View File

@@ -2,6 +2,10 @@ Aidan Thornton:
Added Maple Mini support.
board/board-maple-mini.h
Evangelos Rigas:
Added Gnukey-DS support.
board/board-gnukey-ds.h
Jeremy Drake:
Modified STM32F103 support.
mcu/sys-stm32f103.c
@@ -30,42 +34,56 @@ NIIBE Yutaka:
chopstx.c, chopstx.h,
chopstx-cortex-m.c, chopstx-cortex-m.h,
chopstx-gnu-linux.c, chopstx-gnu-linux.h,
entry.c,
chopstx-riscv32.c, chopstx-riscv32.h,
entry.c, entry-cortex-m.c, entry-riscv32.c,
eventflag.c, eventflag.h
Wrote the drivers mcu/*:
chx-gnu-linux.c, chx-mkl27z.c, chx-stm32f0.c, chx-stm32f103.c,
clk_gpio_init-mkl27z.c, clk_gpio_init-stm32.c,
cortex-m.h, mkl27z.h, stm32.h, stm32f103.h,
Wrote the drivers under mcu/:
chx-gd32vf103.c, chx-gnu-linux.c, chx-mkl27z.c,
chx-stm32f0.c, chx-stm32f103.c, chx-stm32l4.c,
clk_gpio_init-gd32vf103.c, clk_gpio_init-mkl27z.c,
clk_gpio_init-stm32f.c, clk_gpio_init-stm32l.c
cortex-m.h, gd32vf103.h, mkl27z.h, stm32.h,
stm32f103.h, stm32l.h,
sys-gnu-linux.c, sys-gnu-linux.h,
sys-mkl27z.c, sys-mkl27z.h,
sys-stm32f0.c, sys-stm32f0.h
sys-stm32f103.c, sys-stm32f103.h,
usb-stm32f103.c, usb-mkl27z.c
sys-stm32l4.c, sys-stm32l4.h,
usb-gd32vf103.c, usb-mkl27z.c,
usb-st-common.c, usb-stm32f103.c, usb-stm32l4.c
Wrote the drivers:
controb/adc-mkl27z.c
contrib/adc-mkl27z.c,
contrib/spi.h, contrib/spi-st.c
Drew the logo:
chopstx.svg, chopstx.png
Wrote examples:
example-led, example-cdc, example-fsm-55, example-fs-bb48,
example-usb-serial, example-cdc-gnu-linux
example-usb-serial, example-cdc-gnu-linux,
example-usart, example-lcd
Wrote board/*:
board-gnu-linux.h,
board-fst-01sz.h,
board-fst-01g.h, board-fst-01.h, board-fst-01-00.h,
board-olimex-stm32-h103.h, board-stm8s-discovery.h
board-cq-starm.h, board-stbee-mini.h, board-stbee.h,
board-stm32f0-discovery.h, board-fsm-55.h,
board-fs-bb48.h
board-st-nucleo-l432.h,
board-fs-bb48.h,
board-blue-pill-g.h,
board-longan-nano.h
For Free Software Initiative of Japan, wrote:
contrib/adc-stm32f103.c,
contrib/adc-gnu-linux.c
Under contract of g10 Code GmbH, wrote:
mcu/usb-usbip.c
contrib/usart-stm32f103.c
contrib/ackbtn-stm32f103.c
mcu/usb-usbip.c,
contrib/ackbtn.h, contrib/ackbtn-stm32f103.c,
contrib/usart.h,
contrib/usart-common.c, contrib/usart-common-f103.c,
contrib/usart-gd32vf103.c,
contrib/usart-impl-f103.h, contrib/usart-impl.h,
contrib/usart-stm32f103.c, contrib/usart-stm32l4.c,
Paul Fertser:
Added Blue Pill support.

443
ChangeLog
View File

@@ -1,3 +1,446 @@
2021-02-26 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 2.2
* doc/chopstx.texi (VERSION): 2.2.
2021-02-25 NIIBE Yutaka <gniibe@fsij.org>
* rules.mk (MCFLAGS): Add -masm-syntax-unified.
* entry-cortex-m.c (entry): Use Thumb-16 instruction in unified
asm syntax. This means that no output change for Cortex-M0,
but change for Cortex-M3/M4 (shorter, different semantics).
* example-fsm-55/reset.c (reset): Likewise.
* chopstx-cortex-m.c
[__ARM_ARCH_6M__] (involuntary_context_switch): Use unified syntax.
[__ARM_ARCH_7M__] (involuntary_context_switch): Use Thumb-16
instruction.
[__ARM_ARCH_6M__] (chx_handle_intr): Use unified syntax.
[__ARM_ARCH_7M__] (chx_handle_intr): Use Thumb-16 instruction.
[__ARM_ARCH_6M__] (voluntary_context_switch): Use unified syntax.
[__ARM_ARCH_7M__] (svc): Use Thumb-16 instruction.
2021-02-19 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c (chx_handle_intr): Fix SUB instruction.
2021-02-17 NIIBE Yutaka <gniibe@fsij.org>
* contrib/adc-gnu-linux.c (adc_start_conversion): Use getrandom.
* mcu/usb-usbip.c (usbip_run_server): Add start-up message.
2021-02-16 NIIBE Yutaka <gniibe@fsij.org>
* contrib/ackbtn-gnu-linux.c: New.
2021-02-12 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 2.1
* doc/chopstx.texi (VERSION): 2.1.
2021-02-10 NIIBE Yutaka <gniibe@fsij.org>
* example-cdc-gnu-linux/usb-cdc.c (tty_recv): Cancel the input.
* chopstx.c (chx_recv_irq): Bug fix when no waiter.
* chopstx-gnu-linux.c (chopstx_create_arch): Fix ARGC of
makecontext call.
(chx_idle): Support exit by SIGINT or SIGTERM.
2021-02-09 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (chopstx_create_arch): Clear TP.
* example-cdc-gnu-linux/sample.c (main): Handle timeout by canceling
input.
* chopstx-gnu-linux.c (voluntary_context_switch): Fix for the case
when chx_idle suggests no context switch (tp_prev == tp_next).
2021-02-08 NIIBE Yutaka <gniibe@fsij.org>
* entry-cortex-m.c (vector_table): No use of PENDV exception.
Use chx_handle_intr for SysTick.
* example-fsm-55/reset.c (vector): Likewise.
* chopstx-cortex-m.c (preempting): Remove.
(CPU_EXCEPTION_PRIORITY_PENDSV): Remove.
(chx_interrupt_controller_init): No setup for PENDV.
(chx_request_preemption_possibly): Remove.
(involuntary_context_switch): New.
(chx_timer_handler): Remove.
(chx_handle_intr): Use involuntary_context_switch directly.
(preempt): Remove.
2021-02-05 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c (preempting): New value to hold the pointer
to preempting thread, which will become RUNNING.
(chx_request_preemption_possibly): New.
(chx_timer_handler, chx_handle_intr): Use the new function.
(preempt): Recover R0 from PREEMPTING and clear PREEMPTING.
(svc): Don't share return path with preempt.
2021-02-04 NIIBE Yutaka <gniibe@fsij.org>
* board/board-fsm-55.h (VAL_GPIO_LED_MODER): Support SWD
debugging.
2020-06-26 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 2.0
* doc/chopstx.texi (VERSION): 2.0.
2020-06-23 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chopstx_critical): New.
2019-12-27 NIIBE Yutaka <gniibe@fsij.org>
* contrib/spi-st.c (check_transmit, put_data): Fix handling
conditions.
[!SPI_USE_INTERRUPT]: Support busy wait mode.
2019-12-27 NIIBE Yutaka <gniibe@fsij.org>
* mcu/usb-gd32vf103.c: New.
* mcu/clk_gpio_init-gd32vf103.c (clock_init): Configure USB clock.
* mcu/gd32vf103.h (RCU_CFG0_USBFSPSC_DIV2): New.
* usb_lld.h [MCU_GD32VF1]: Support GD32VF103.
2019-12-27 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chopstx_poll): Call CHECK for condition
valiable after woken up.
2019-12-04 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (chx_thread_start): Fix return value.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* rules.mk [ARCH=riscv32] (MCFLAGS, LDFLAGS): Define.
(CFLAGS, LDFLAGS): Don't add -m for ARM specific.
* example-lcd: New.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart-gd32vf103.c: New.
* contrib/usart-common.c (usart_stat, get_usart_dev)
(get_usart_rb_h2a, get_usart_rb_a2h, get_usart_intr): Handle
when USART_DEVNO_START==0.
* contrib/usart-stm32f103.c: Use usart-impl.h.
Factor out to...
* contrib/usart-common-f103.c: New.
* contrib/usart-stm32l4.c: Factor out definitions into...
* contrib/usart-impl.h: New.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* board/board-longan-nano.h: New.
* mcu/gd32vf103.h: New.
* mcu/chx-gd32vf103.c: New.
* mcu/clk_gpio_init-gd32vf103.c: New.
* chopstx-riscv32.h: New.
* chopstx-riscv32.c: New.
* entry-riscv32.c: New.
* entry-cortex-m.c: Rename from entry.c.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* contrib/spi.h: New.
* contrib/spi-st.c: New.
* chopstx.c (chx_timer_expired): When no timer, explicitly call
chx_systick_reload with 0.
2019-12-03 NIIBE Yutaka <gniibe@fsij.org>
* example-cdc/Makefile (ARCH): Define.
* example-cdc-gnu-linux/Makefile (ARCH): Ditto.
* example-fraucheky/Makefile (ARCH): Ditto.
* example-fs-bb48/Makefile (ARCH): Ditto.
* example-fsm-55/Makefile (ARCH): Ditto.
* example-led/Makefile (ARCH): Ditto.
* example-primer2/Makefile (ARCH): Ditto.
* example-usart/Makefile (ARCH): Ditto.
* example-usb-serial/Makefile (ARCH): Ditto.
* chopstx.c: Include files by ARCH_HEADER and ARCH_IMPL.
* rules.mk (DEFS): Define ARCH_HEADER and ARCH_IMPL by ARCH.
2019-11-25 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (chx_idle): Synchronous IDLE.
(chx_idle, idle_stack): Remove.
(chx_init_arch): Don't makecontext for IDLE.
(preempted_context_switch): Simplify, because no case for
interrupting IDLE.
(voluntary_context_switch): Simply call chx_idle to get tp_next.
2019-11-22 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (preempted_context_switch): Rename.
2019-11-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chx_sched): New common function.
* chopstx-cortex-m.c (svc): Simply context switch.
(voluntary_context_switch): New, replacing chx_sched.
* chopstx-gnu-linux.c (voluntary_context_switch): Likewise.
2019-11-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.h (CHOPSTX_PRIO_INHIBIT_PREEMPTION): Remove.
* chopstx.c (chx_init): Remove support of
CHOPSTX_PRIO_INHIBIT_PREEMPTION.
(chopstx_setpriority): Likewise.
* chopstx-cortex-m.c (CPU_EXCEPTION_PRIORITY_PENDSV): Change the
value. Now PendSV exception runs at same priority level of
all other exceptions (systick and interrupt exception).
(chx_cpu_sched_lock, chx_cpu_sched_unlock):
Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
(chx_request_preemption): Change the argument with none.
(chx_timer_handler): New. Use common function of
chx_timer_expired, local one, chx_request_preemption.
(chx_handle_intr): Use chx_recv_irq.
(chx_sched): Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
(preempt): Have an argument from tail-chaining.
Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION.
* chopstx-gnu-linux.c (chx_idle): Rename.
(chx_init_arch): Follow the rename.
(chx_handle_intr): Use chx_recv_irq, chx_running_preempted, and
chx_preempt_into.
(sigalrm_handler): Use chx_timer_expired, chx_running_preempted,
and chx_preempt_into.
(chx_preempt_into): Rename from chx_request_preemption, changing
argument. Remove the handling of preempted thread, which should
be done by caller using chx_running_preempted, beforehand.
* entry.c (vector_table): Use chx_timer_handler.
* chopstx.c (chx_timer_expired): Pop from the ready queue and
return the next thread to switch.
(chx_recv_irq): New.
(chx_running_preempted): New.
2019-11-21 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c (chx_set_running): New.
(chx_init_arch): Use chx_set_running.
(preempt, svc): Remove set to running, not needed.
* chopstx-gnu-linux.c (chx_set_running): New.
(chx_init_arch, chx_request_preemption, chx_sched): Use
chx_set_running and chx_running.
(chx_sched): Bug fix of return value handling.
2019-11-20 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.17.
* doc/chopstx.texi (VERSION): 1.17.
* chopstx-gnu-linux.c (chx_running): New.
(chx_init_arch): Set RUNNING.
2019-11-19 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c (chx_running): New.
(chx_init_arch): Set RUNNING.
* chopstx.c (chx_init): Don't set RUNNING here.
(chx_timer_expired): Use chx_running.
(chx_systick_init, chx_exit, chx_mutex_unlock, chopstx_create)
(chopstx_mutex_lock, chopstx_mutex_unlock, chopstx_cleanup_push)
(chopstx_cleanup_pop, chopstx_exit, chopstx_cancel)
(chopstx_testcancel, chopstx_setcancelstate, chx_proxy_init)
(chopstx_poll, chopstx_setpriority): Likewise.
2019-11-18 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (chx_systick_init_arch): Rename.
(chx_interrupt_controller_init): Rename.
* chopstx-cortex-m.c (chx_systick_init_arch): Rename.
(chx_interrupt_controller_init): Rename.
* chopstx.c (chx_systick_init): Use chx_systick_init_arch.
(chx_init): Use chx_interrupt_controller_init.
2019-11-18 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chx_ready_pop): Check flag_sched_rr here.
* chopstx-cortex-m.c (chx_sched) [__ARM_ARCH_6M__]: Use
new interface of chx_ready_pop.
(preempt,svc): Likewise.
2019-11-18 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c (ticks_to_usec): New.
* chopstx-gnu-linux.c (ticks_to_usec): New.
* chopstx.c (chx_snooze): Use ticks_to_usec.
2019-11-18 NIIBE Yutaka <gniibe@fsij.org>
* rules.mk (CSRC): Change the rule of entry*.c.
* entry.c: It's only for Cortex-M, now.
* entry-gnu-linux.c: New file.
2019-11-18 NIIBE Yutaka <gniibe@fsij.org>
* entry.c: Use chopstx-cortex-m.h.
* chopstx.h (CHOPSTX_THREAD_SIZE): Move the definition to ...
* chopstx-cortex-m.h (CHOPSTX_THREAD_SIZE): ... here.
2019-10-07 Jeremy Drake <jeremy@drastrom.science>
* mcu/usb-st-common.c (usb_lld_init): Move BTABLE initialization
after clearing ISTR register.
2019-09-04 NIIBE Yutaka <gniibe@fsij.org>
When it was exactly 64-byte, two ZLPs were sent wrongly.
* mcu/usb-st-common.c (usb_lld_ctrl_send): Fix for 64-byte.
* mcu/usb-usbip.c (usb_lld_ctrl_send): Likewise.
* mcu/usb-mkl27z.c (usb_lld_ctrl_send): Likewise.
2019-05-22 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.16.
* doc/chopstx.texi (VERSION): 1.16.
2019-05-22 Evangelos Rigas <erigas@rnd2.org>
* board/board-gnukey-ds.h: New.
2019-05-14 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.15.
* doc/chopstx.texi (VERSION): 1.15.
2019-05-13 NIIBE Yutaka <gniibe@fsij.org>
* board/board-maple-mini.h: Assert USB D+ pull-up.
* board/board-olimex-stm32-h103.h: Likewise.
* board/board-stbee.h:Likewise.
* board/board-stm32-primer2.h: Likewise.
* mcu/usb-stm32f103.c: Don't use usb_lld_sys_init and
usb_lld_sys_shutdown in SYS.
* mcu/usb-stm32l4.c: Ditto.
2019-05-10 NIIBE Yutaka <gniibe@fsij.org>
* chopstx.c (chopstx_claim_irq): Check INTR before the call.
* chopstx-cortex-m.c (chx_disable_intr): Have return value.
* chopstx-gnu-linux.c (chx_disable_intr): Likewise.
2019-05-10 NIIBE Yutaka <gniibe@fsij.org>
* mcu/sys-stm32f103.c: SYS version 4.0.
* mcu/sys-gnu-linux.c: Likewise.
* mcu/sys-mkl27z.c: Likewise.
* mcu/sys-stm32l4.c: Likewise.
* mcu/sys-stm32f0.c: Likewise.
2019-05-08 NIIBE Yutaka <gniibe@fsij.org>
* mcu/sys-stm32f103.c (usb_lld_sys_shutdown, usb_lld_sys_init):
Only cable config.
* mcu/sys-stm32l4.c (usb_lld_sys_shutdown, usb_lld_sys_init):
Likewise.
* mcu/usb-st-common.c (usb_lld_init): Call chip specific routine.
(usb_lld_shutdown): Likewise.
* mcu/usb-stm32l4.c (usb_lld_init_chip_specific)
(usb_lld_shutdown_chip_specific): New.
* mcu/usb-stm32f103.c (usb_lld_init_chip_specific)
(usb_lld_shutdown_chip_specific): New.
* rules.mk (DEFS): Add -DUSE_SYS when USE_SYS.
2019-04-25 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart.h (BSCARD1, BSCARD2...): New.
(BSCARD): Remove.
* contrib/usart-stm32f103.c (usart_config_baud): Rename
from usart_config_brr, changing API.
* contrib/usart-stm32l4.c (usart_config_baud): Likewise.
2019-04-24 NIIBE Yutaka <gniibe@fsij.org>
* mcu/sys-stm32l4.c: New.
* mcu/chx-stm32l4.c: New. Not support suspend yet.
* mcu/stm32l.h: New.
* mcu/clk_gpio_init-stm32l.c: New.
2019-04-17 NIIBE Yutaka <gniibe@fsij.org>
* mcu/usb-stm32l4.c: New.
* mcu/usb-st-common.c: Factor out from usb-stm32f103.c.
2019-04-12 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart-stm32l4.c: New.
* contrib/usart-common.c: Factor out from usart-stm32f103.c.
* entry.c [__ARM_ARCH_7EM__] (entry): Fix for Cortex-M4.
2019-04-11 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-cortex-m.c: Support Cortex-M4 (not touching FPU).
2019-04-10 NIIBE Yutaka <gniibe@fsij.org>
* mcu/clk_gpio_init-stm32f.c: Rename from clk_gpio_init-stm32.c.
* entry.c: Use STM32F10X_HD.
* board/board-stbee.h (STM32F10X_HD): Add.
* board/board-stm32-primer2.h (STM32F10X_HD): Add.
2019-04-09 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart-stm32f103.c (usart_block_sendrecv): Support
busy-wait when timeout_block_p == 0.
2019-04-08 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart-stm32f103.c (usart_block_sendrecv): New.
(usart_init0): New.
2019-03-30 Niibe Yutaka <gniibe@fsij.org>
* contrib/usart-stm32f103.c (usart_config_brr): New.
(usart_config_re): New internal function.
(handle_intr, handle_tx_ready): Use TC for smartcard.
(usart_wait_write_completion): New internal function.
(usart_write): Support half-duplex smartcard mode.
2019-03-29 NIIBE Yutaka <gniibe@fsij.org>
* contrib/usart-stm32f103.c (usart_read_ext): New.
(usart_read_prepare_poll): New.
2019-03-02 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.14.

View File

@@ -5,7 +5,7 @@ include following exception to GNU GPL.
--------------------
As additional permission under GNU GPL version 3 section 7, you may
distribute non-source form of the Program without the copy of the GNU
GPL normally required by section 4, provided you inform the receipents
GPL normally required by section 4, provided you inform the recipients
of GNU GPL by a written offer.
--------------------

136
NEWS
View File

@@ -1,6 +1,142 @@
NEWS - Noteworthy changes
* Major changes in Chopstx 2.2
Released 2021-02-25
** Acknowledge button support for GNU/Linux emulation
User is asked acknowledge by RET with standard input.
** USB driver for GNU/Linux emulation
It shows start-up message now.
** ADC driver for GNU/Linux emulation
It uses getrandom(2), output is not deterministic any more.
** Bug fix for Cortex-M3/M4 Support
In 2.1, preemption doesn't work well, because of the difference of
assembler syntax.
* Major changes in Chopstx 2.1
Released 2021-02-12
** GNU/Linux emulation change
The process can be asked to exit by SIGINT or SIGTERM.
** Bug fix for interrupt handling
The check to find waiting thread was wrong. If no waiting thread,
it failed.
** Bug fix for GNU/Linux emulation
In 2.0, GNU/Linux emulation doesn't work well with chx_idle when it
handles interrupt synchronously and the waken thread is the same one
which called chx_idle.
** Bug fix for Cortex-M0/M3/M4 Support
In 2.0, Cortex-M0 with no tail-chaining support (e.g. STM32F030)
doesn't work. In 2.0, Cortex-M3/M4 may fail when two or more
interrupts occur simultaneously; A waken thread (which is about to
preempt RUNNING) by the first interrupt may be lost (and never
scheduled again), by the second interrupt handling before the call of
preempt function.
* Major changes in Chopstx 2.0
Released 2020-06-26
** Remove support of CHOPSTX_PRIO_INHIBIT_PREEMPTION
We used to have (an experimental) feature of higher priority of thread
(>= CHOPSTX_PRIO_INHIBIT_PREEMPTION). The feature offered: When a
thread has such a priority, Chostx guarantees that it keeps running
and no other threads can preempt the thread. I had introduced this
(questionable) feature as an excuse against no support of
masking/unmasking interrupt API. From the experience of Chopstx, it
is rather consistent to remove this feature. It was over engineering
to support a thread having such a special priority.
** New function: chopstx_critical
Let run FUNC with ARG, under CPU scheduler lock (== no preemption).
** RISC-V MCU support
RISC-V support has been added. It's for the core named Bumblebee.
* Major changes in Chopstx 1.18
Released 2019-12-30
** Fix of chopstx_poll
When waiting for a condition variable, we supply CHECK method with a
descriptor. Since a condition variable may be fired for multiple
reasons, old implementation of chopstx_poll may return with wrong
information saying a condition of CHECK were met but actually not. It
should not return when condition is not satisfied and it should not
give wrong information to application. Fixed by calling the CHECK
method again when woken up, and don't return when no condition meet.
** Bug fix for GNU/Linux emulation
When woken up, return value of chx_sched was wrong. Because of this,
timeout handling had problem. Termination value of a thread was
wrong.
* Major changes in Chopstx 1.17
Released 2019-11-20
** USB drivers bug fix for STM32 and ZLP handling for 64-byte packet.
* Major changes in Chopstx 1.16
Released 2019-05-22
** New board support: Gnukey-DS
It is contributed by Evangelos Rigas.
* Major changes in Chopstx 1.15
Released 2019-05-14
** SYS version 4.0
USB initialization/finalization API has been changed. SYS routines
only handle USB cable configuration when supported by a board.
** USB driver change
Enabling/disabling USB module is done by USB driver. It's
responsibility of board configuration to enable external transistor
for pull-up D+-line by board/*.h. For all boards, USB driver doesn't
use usb_lld_sys_init or usb_lld_sys_shutdown (those routines only can
be used for USB self powered system, which Chopstx does not support
yet).
** Board configuration change
For USB driver change, board-maple-mini.h, board-olimex-stm32-h103.h,
board-stbee.h, and board-stm32-primer2.h were changed. Pull-up is
enabled at the time of gpio_init.
** Cortex-M4 support
Cortex-M4 support has been added. Not supporting use of FPU or DSP,
yet. Note that it's not intend to be used by Gnuk.
** STM32L432 support
USART and USB drivers are added.
** New board support: ST Nucleo-32 L432
ST Nucleo-32 L432 is a small board with ST-Link/V2-1.
** Minor implementation change: chopstx_claim_irq
If the interrupt is disabled (possibly by chx_intr) when
chopstx_claim_irq is called, INTR->ready is set to 1. This allows
calling chopstx_claim_irq after hardware initialization which may
cause an interrupt before the call of chopstx_claim_irq.
* Major changes in Chopstx 1.14
Released 2019-03-02

11
README
View File

@@ -1,6 +1,6 @@
Chopstx - Threads and only Threads
Version 1.14
2018-03-02
Version 2.2
2020-02-26
Niibe Yutaka
Flying Stone Technology
@@ -8,8 +8,9 @@ What's Chopstx?
===============
Chopstx is an RT thread library for STM32F103 and GD32F103 (ARM
Cortex-M3), STM32F030 (ARM Cortex-M0), MKL27Z (ARM Cortex-M0plus), and
emulation on GNU/Linux.
Cortex-M3), STM32F030 (ARM Cortex-M0), MKL27Z (ARM Cortex-M0plus),
STM32L432 (ARM Cortex-M4), GD32VF103 (RISC-V Bumblebee) and emulation
on GNU/Linux.
While most RTOSes come with many features, drivers, and protocol
stacks, Chopstx just offers a simple RT thread library.
@@ -19,7 +20,7 @@ enables coherent code for ease of maintenance.
While threads are important, we don't need more threads than
necessary. Chopstx provides a feature of poll, so that we can
minimize use of threads.
handle multiple events by a single thread.
Note that this library is _not_ related to the hand game:

View File

@@ -1 +1 @@
release/1.14
release/2.1

View File

@@ -19,10 +19,13 @@
/*
* Port A setup.
* PA5 - ON (LED 1:ON 0:OFF)
* PA4 - Pull DOWN
* PA5 - ON (LED 1:ON 0:OFF)
*
* PA13 - SWDIO
* PA14 - SWCLK
*/
#define VAL_GPIO_LED_MODER 0x00145555 /* Output Pin0-7, Pin9 and Pin10 */
#define VAL_GPIO_LED_MODER 0x28145555 /* Output Pin0-7, Pin9 and Pin10 */
#define VAL_GPIO_LED_OTYPER 0x0000001f /* Open-drain for Pin0-4, Push-Pull*/
#define VAL_GPIO_LED_OSPEEDR 0x003cffff /* High speed */
#define VAL_GPIO_LED_PUPDR 0x00000000 /* No pull-up/pull-down */

View File

@@ -19,7 +19,7 @@
* PA0 - input with pull-up. AN0
* PA1 - input with pull-up. AN1
* PA8 - Push pull output 50MHz (LED 1:ON 0:OFF)
* PA10 - Push pull output 50MHz (USB 1:ON 0:OFF)
* PA10 - Push pull output 50MHz (USB 1:ON 0:OFF) default 1
* PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP)
* ------------------------ Default

View File

@@ -25,7 +25,7 @@
* PA5 - Alternate Push pull output (SPI1_SCK)
* PA6 - Alternate Push pull output (SPI1_MISO)
* PA7 - Alternate Push pull output (SPI1_MOSI)
* PA10 - Push pull output (USB 1:ON 0:OFF)
* PA10 - Push pull output (USB 1:ON 0:OFF) default 1
* PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP)
* ------------------------ Default

39
board/board-gnukey-ds.h Normal file
View File

@@ -0,0 +1,39 @@
#define BOARD_NAME "Gnukey DS"
#define BOARD_ID 0x67ee65a3
/* echo -n "Gnukey DS" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */
#define MCU_STM32F1 1
#define STM32F10X_MD /* Medium-density device */
#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1
#define STM32_PLLMUL_VALUE 9 // 8MHz * 9 = 72 MHz
#define STM32_HSECLK 8000000
#define GPIO_LED_BASE GPIOA_BASE
#define GPIO_LED_SET_TO_EMIT 3
#define GPIO_USB_BASE GPIOA_BASE
#undef GPIO_OTHER_BASE
/*
* Port A setup.
* PA0 - input with pull-up: AN0 for NeuG
* PA1 - input with pull-up: AN1 for NeuG
* PA2 - input with pull-up: Hall effect sensor output
* PA3 - Push pull output 10MHz 0 default (LED 1:ON 0:OFF)
* PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP)
* ------------------------ Default
* PAx - input with pull-up.
*/
#define VAL_GPIO_LED_ODR 0xFFFFE7F7 /* 0/1 Pull Down/Up */
#define VAL_GPIO_LED_CRL 0x88881888 /* PA7...PA0 */
#define VAL_GPIO_LED_CRH 0x88811888 /* PA15...PA8 */
#define RCC_ENR_IOP_EN RCC_APB2ENR_IOPAEN
#define RCC_RSTR_IOP_RST RCC_APB2RSTR_IOPARST
/*
* Board specific information other than clock and GPIO initial
* setting should not be in board-*.h, but each driver should include
* such specific information by itself.
*/

75
board/board-longan-nano.h Normal file
View File

@@ -0,0 +1,75 @@
#define BOARD_NAME "Longan-nano"
#define BOARD_ID 0xe65dace7
/* echo -n "Longan-nano" | sha256sum | sed -e 's/^.*\(........\) -$/\1/' */
#define MCU_CORE_BUMBLEBEE 1
#define MCU_GD32VF1 1
/*
* XTAL clock = 8 MHz
* Core clock = 96 MHz
* AHB clock = 96 MHz
* APB1 clock = 48 MHz
* APB2 clock = 96 MHz
* USB clock = 48 MHz
* ADC clock = ???
*/
/* HXTAL 8MHz input => PLL output = 8 * 12 = 96 MHz */
#define RCU_CFG0_PLL_MUL_VALUE RCU_CFG0_PLL_MUL12
#define GPIO_LED GPIOC
#define GPIO_LED_CLEAR_TO_EMIT 13 /* LED Red */
#define GPIO_USB GPIOA
#define GPIO_OTHER GPIOB
/*
* Port A setup.
*
* PA1 - Push-pull output 2MHz 1 default (LED green 0: ON 1: OFF)
* PA2 - Push-pull output 2MHz 1 default (LED blue 0: ON 1: OFF)
* PA3 -
* PA4 - <Can be used for GPIO>
* PA5 - AF output push-pull 50MHz: SPI0_SCK
* PA6 - Input pull-up: SPI0_MISO (master)
* PA7 - AF output push-pull 50MHz: SPI0_MOSI (master)
* PA8 - (USBFS_SOF)
* PA9 - AF output push-pull 2MHz: USART0_TX (USBFS_VBUS)
* PA10 - Input pull-up: USART0_RX0 (USBFS_ID)
* PA11 - Push Pull output 10MHz 0 default (until USB enabled???) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled???) (USBDP)
* ------------------------ Default
* PAx - input with pull-up.
*
* NOTE: It's USB device bus power configuration, where we don't
* monitor VBUS voltage change. For self powered system, we need to
* monitor VBUS pin for detecting USB connection. Supporting self
* powered system, you need to change the USB driver, and when doing
* so, PA9 should be used for USBFS_VBUS.
*/
#define VAL_GPIO_USB_ODR 0xFFFFE6FF
#define VAL_GPIO_USB_CRL 0xB8B88228 /* PA7...PA0 */
#define VAL_GPIO_USB_CRH 0x888118A8 /* PA15...PA8 */
#define RCU_APB2_GPIO (RCU_APB2_GPIOA|RCU_APB2_GPIOB|RCU_APB2_GPIOC)
/*
* PB0 - Push-pull output 50MHz 1 default (OLED D/C#)
* PB1 - Push-pull output 10MHz 1 default (OLED RST#)
* PB2 - Push-pull output 50MHz 1 default (OLED CS#)
*/
#define VAL_GPIO_OTHER_ODR 0xFFFFFFFF
#define VAL_GPIO_OTHER_CRL 0x88888313 /* PB7...PB0 */
#define VAL_GPIO_OTHER_CRH 0x88888888 /* PB15...PB8 */
/*
* PC13 - Push-pull output 2MHz 1 default (LED red 0: ON 1: OFF)
*/
#define VAL_GPIO_LED_ODR 0xFFFFFFFF
#define VAL_GPIO_LED_CRL 0x88888888 /* PC7...PC0 */
#define VAL_GPIO_LED_CRH 0x88288888 /* PC15...PC8 */
/*
* Board specific information other than clock and GPIO initial
* setting should not be in board-*.h, but each driver should include
* such specific information by itself.
*/

View File

@@ -30,11 +30,11 @@
/*
* Port B setup.
* PB1 - Push pull output 50MHz (LED 1:ON 0:OFF)
* PB9 - Push pull output 50MHz (USB 1:ON 0:OFF)
* PB9 - Push pull output 50MHz (USB 0:ON 1:OFF) default 0
* ------------------------ Default
* PBx - input with pull-up
*/
#define VAL_GPIO_LED_ODR 0xFFFFFFFF
#define VAL_GPIO_LED_ODR 0xFFFFFDFF
#define VAL_GPIO_LED_CRL 0x88888838 /* PB7...PB0 */
#define VAL_GPIO_LED_CRH 0x88888838 /* PB15...PB8 */

View File

@@ -29,7 +29,7 @@
* PA10 - floating input
* PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP)
* PA15 - Push pull output (USB_EN 1:ON 0:OFF)
* PA15 - Push pull output (USB_EN 1:ON 0:OFF) default 1
* ------------------------ Default
* PA8 - input with pull-up.
* PA9 - floating input.

View File

@@ -20,12 +20,12 @@
* PC1 - input with pull-up. AN11 for NeuG
* PC6 - input without pull-up/down
* PC7 - input without pull-up/down
* PC11 - Open-drain output 50MHz (USB disconnect).
* PC11 - Open-drain output 50MHz (USB 0:ON 1:OFF) default 0
* PC12 - Push Pull output 50MHz (LED).
* ------------------------ Default
* PCx - input with pull-up
*/
#define VAL_GPIO_LED_ODR 0xFFFFFFFF
#define VAL_GPIO_LED_ODR 0xFFFFF7FF
#define VAL_GPIO_LED_CRL 0x44888888 /* PC7...PC0 */
#define VAL_GPIO_LED_CRH 0x88837888 /* PC15...PC8 */

View File

@@ -22,7 +22,7 @@
* PA9 - Push pull output 50MHz (LED 1:ON 0:OFF)
* PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP)
* PA15 - Push pull output 50MHz (USB 1:ON 0:OFF)
* PA15 - Push pull output 50MHz (USB 1:ON 0:OFF) default 1
* ------------------------ Default
* PAx - input with pull-up
*/

View File

@@ -11,8 +11,10 @@
*
* At CN10, connect USB cable
* Vbus RED --> 10 NC ----------> CN7 (6 E5V)
* D+ GREEN --> 12 PA11 ---[1K5]--> CN6 (4 3V3)
* D- WHITE --> 14 PA12
* D+ GREEN --> 12 PA12 ---[1K5]--> CN6 (4 3V3)
* D- WHITE --> 14 PA11
* 16 PB12 (USART3-CK) ---> smartcard CK
* 18
* GND BLACK --> 20 GND
*/
@@ -70,5 +72,5 @@
#define VAL_GPIO_OTHER_CRL 0x82888888 /* PB7...PB0 */
#define VAL_GPIO_OTHER_CRH 0x8B8B8F22 /* PB15...PB8 */
#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN)
#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST)
#define RCC_ENR_IOP_EN (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN)
#define RCC_RSTR_IOP_RST (RCC_APB2RSTR_IOPARST | RCC_APB2RSTR_IOPBRST | RCC_APB2RSTR_AFIORST)

View File

@@ -0,0 +1,75 @@
#define BOARD_NAME "ST Nucleo L432"
#define BOARD_ID 0x3a8d5116
/*
* When using USB, please add USB cable to ST Nucleo L432.
*
* At CN4, connect USB cable, only when ST Link is not connected
* Vbus RED --> 4
*
* At CN3, connect USB cable
* D- WHITE --> 13 PA11
* D+ GREEN --> 5 PA12
* GND BLACK --> 4 GND
*
* Smartcard VCC: PA3
* Smartcard GND: GND --+
* Smartcard RST: PA1 |
* Smartcard VPP: PA4 |
* Smartcard CLK: PA8 |
* Smartcard I/O: PA9 |
* GND --+
* Smartcard DETECT: PA0
*/
#define MCU_STM32L4 1
#define GPIO_LED_BASE GPIOB_BASE
#define GPIO_LED_SET_TO_EMIT 3
#undef GPIO_USB_BASE /* No external DISCONNECT/RENUM circuit. */
#define GPIO_OTHER_BASE GPIOA_BASE
/*
* Port A setup.
*
* MODER: 10 10 - 10 10 - 10 11 - 10 10 11 11 - 11 01 - 01 10 - 01 00
*
* PA0 - Input with pull-up: Card insertion detect: 0 when detected
* PA1 - Output push-pull default 0: Card RST
* PA2 - USART2-TX (AF7): output push-pull
* PA3 - Output push-pull default 0: Card Vcc
* PA4 - Output push-pull default 0: Card Vpp
* PA8 - USART1-CK (AF7): output push-pull: Card CLK
* PA9 - USART1-TX (AF7): output(input) Open-drain pull-up: Card I/O
* PA11 - USBDM (AF10): input/output
* PA12 - USBDP (AF10): input/output
* PA13 - SWDIO (AF0)
* PA14 - SWDCLK (AF0)
* PA15 - USART2-RX (AF3): input with pull-up
* ------------------------ Default
* PAx - analog input
*/
#define VAL_GPIO_OTHER_MODER 0xAABAFD64
#define VAL_GPIO_OTHER_OTYPER 0x00000200
#define VAL_GPIO_OTHER_OSPEEDR 0xFFFFFFFF
#define VAL_GPIO_OTHER_PUPDR 0x40040001
#define VAL_GPIO_OTHER_AFRL 0x00000700
#define VAL_GPIO_OTHER_AFRH 0x300AA077
/*
* Port B setup.
*
* MODER: 11 11 - 11 11 - 11 11 - 11 11 11 11 - 11 11 - 01 11 - 11 11
*
* PB3 - ON (LED 1:ON 0:OFF)
* ------------------------ Default
* PBx - analog input
*/
#define VAL_GPIO_LED_MODER 0xFFFFFF7F
#define VAL_GPIO_LED_OTYPER 0x00000000
#define VAL_GPIO_LED_OSPEEDR 0x00000000
#define VAL_GPIO_LED_PUPDR 0x00000000
#define VAL_GPIO_LED_AFRL 0x00000000
#define VAL_GPIO_LED_AFRH 0x00000000
#define RCC_AHB2_GPIO (RCC_AHB2_GPIOA | RCC_AHB2_GPIOB)

View File

@@ -52,7 +52,7 @@
* PA11 - Push Pull output 10MHz 0 default (until USB enabled) (USBDM)
* PA12 - Push Pull output 10MHz 0 default (until USB enabled) (USBDP)
* PA13 - Open Drain output (LED1 0:ON 1:OFF)
* PA14 - Push pull output (USB ENABLE 0:DISABLE 1:ENABLE)
* PA14 - Push pull output (USB 1:ON 0:OFF) default 1
* PA15 - Open Drain output (LED2 0:ON 1:OFF)
*/
#define VAL_GPIO_LED_ODR 0xFFFFE77F

View File

@@ -2,7 +2,7 @@
#define BOARD_ID 0x945c37e8
#define MCU_STM32F1 1
/* High-density device */
#define STM32F10X_HD /* High-density device */
#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1
#define STM32_PLLMUL_VALUE 6
@@ -29,9 +29,9 @@
/*
* Port D setup.
* PD3 - Push pull output (USB_DISC 1:USB-DISABLE 0:USB-ENABLE) 2MHz
* PD3 - Push pull output (USB 1:OFF 0:ON) 2MHz default 0
* PD4 - Open Drain output 2MHz (LED1).
*/
#define VAL_GPIO_LED_ODR 0xFFFFFFFF
#define VAL_GPIO_LED_ODR 0xFFFFFFF7
#define VAL_GPIO_LED_CRL 0x88862888 /* PD7...PD0 */
#define VAL_GPIO_LED_CRH 0x88888888 /* PD15...PD8 */

View File

@@ -2,7 +2,7 @@
#define BOARD_ID 0x21e5798d
#define MCU_STM32F1 1
/* High-density device */
#define STM32F10X_HD /* High-density device */
#define STM32_PLLXTPRE STM32_PLLXTPRE_DIV1
#define STM32_PLLMUL_VALUE 6
@@ -30,11 +30,11 @@
/*
* Port D setup.
* PD3 - Push pull output 50MHz (USB 1:ON 0:OFF)
* PD3 - Push pull output 50MHz (USB 0:ON 1:OFF) default 0
* ------------------------ Default
* PDx - input with pull-up
*/
#define VAL_GPIO_USB_ODR 0xFFFFFFFF
#define VAL_GPIO_USB_ODR 0xFFFFFFF7
#define VAL_GPIO_USB_CRL 0x88883888 /* PD7...PD0 */
#define VAL_GPIO_USB_CRH 0x88888888 /* PD15...PD8 */

View File

@@ -1,8 +1,8 @@
/*
* chopstx-cortex-m.c - Threads and only threads: Arch specific code
* for Cortex-M0/M3
* for Cortex-M0/M3/M4
*
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2021
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -24,10 +24,25 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
static struct chx_thread *running;
static struct chx_thread *
chx_running (void)
{
return running;
}
static void
chx_set_running (struct chx_thread *r)
{
running = r;
}
/* Data Memory Barrier. */
static void
chx_dmb (void)
@@ -62,7 +77,6 @@ struct chx_stack_regs {
* Prio 0x40: thread temporarily inhibiting schedule for critical region
* ...
* Prio 0xb0: systick, external interrupt
* Prio 0xc0: pendsv
* =====================================
*
* Cortex-M0
@@ -70,8 +84,7 @@ struct chx_stack_regs {
* Prio 0x00: thread temporarily inhibiting schedule for critical region
* ...
* Prio 0x40: systick, external interrupt
* Prio 0x80: pendsv
* Prio 0x80: svc
* Prio 0x80: svc (not used)
* =====================================
*/
@@ -80,18 +93,16 @@ struct chx_stack_regs {
#if defined(__ARM_ARCH_6M__)
#define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x00
/* ... */
#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT
#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0x40
#define CPU_EXCEPTION_PRIORITY_PENDSV 0x80
#define CPU_EXCEPTION_PRIORITY_SYSTICK 0x40
#define CPU_EXCEPTION_PRIORITY_INTERRUPT CPU_EXCEPTION_PRIORITY_SYSTICK
#define CPU_EXCEPTION_PRIORITY_SVC 0x80 /* No use in this arch */
#elif defined(__ARM_ARCH_7M__)
#elif defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
#define CPU_EXCEPTION_PRIORITY_SVC 0x30
#define CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED 0x40
/* ... */
#define CPU_EXCEPTION_PRIORITY_SYSTICK CPU_EXCEPTION_PRIORITY_INTERRUPT
#define CPU_EXCEPTION_PRIORITY_INTERRUPT 0xb0
#define CPU_EXCEPTION_PRIORITY_PENDSV 0xc0
#define CPU_EXCEPTION_PRIORITY_SYSTICK 0xb0
#define CPU_EXCEPTION_PRIORITY_INTERRUPT CPU_EXCEPTION_PRIORITY_SYSTICK
#else
#error "no support for this arch"
#endif
@@ -106,30 +117,34 @@ struct chx_stack_regs {
* System tick
*/
/* SysTick registers. */
static volatile uint32_t *const SYST_CSR = (uint32_t *)0xE000E010;
static volatile uint32_t *const SYST_RVR = (uint32_t *)0xE000E014;
static volatile uint32_t *const SYST_CVR = (uint32_t *)0xE000E018;
struct SYST {
volatile uint32_t CSR;
volatile uint32_t RVR;
volatile uint32_t CVR;
const uint32_t CALIB;
};
static struct SYST *const SYST = (struct SYST *)0xE000E010;
static void
chx_systick_reset (void)
chx_systick_init_arch (void)
{
*SYST_RVR = 0;
*SYST_CVR = 0;
*SYST_CSR = 7;
SYST->RVR = 0;
SYST->CVR = 0;
SYST->CSR = 7;
}
static void
chx_systick_reload (uint32_t ticks)
{
*SYST_RVR = ticks;
*SYST_CVR = 0; /* write (any) to clear the counter to reload. */
*SYST_RVR = 0;
SYST->RVR = ticks;
SYST->CVR = 0; /* write (any) to clear the counter to reload. */
SYST->RVR = 0;
}
static uint32_t
chx_systick_get (void)
{
return *SYST_CVR;
return SYST->CVR;
}
static uint32_t usec_to_ticks (uint32_t usec)
@@ -137,6 +152,12 @@ static uint32_t usec_to_ticks (uint32_t usec)
return usec * MHZ;
}
static uint32_t
ticks_to_usec (uint32_t ticks)
{
return ticks / MHZ;
}
/*
* Interrupt Handling
*/
@@ -175,10 +196,13 @@ chx_clr_intr (uint8_t irq_num)
NVIC_ICPR (irq_num) = 1 << (irq_num & 0x1f);
}
static void
static int
chx_disable_intr (uint8_t irq_num)
{
int already_disabled = !!(NVIC_ICER (irq_num) & (1 << (irq_num & 0x1f)));
NVIC_ICER (irq_num) = 1 << (irq_num & 0x1f);
return already_disabled;
}
static void
@@ -198,20 +222,17 @@ static uint32_t *const SHPR2 = (uint32_t *)0xE000ED1C;
static uint32_t *const SHPR3 = (uint32_t *)0xE000ED20;
static void
chx_prio_init (void)
chx_interrupt_controller_init (void)
{
*AIRCR = 0x05FA0000 | ( 5 << 8); /* PRIGROUP = 5, 2-bit:2-bit. */
*SHPR2 = (CPU_EXCEPTION_PRIORITY_SVC << 24);
*SHPR3 = ((CPU_EXCEPTION_PRIORITY_SYSTICK << 24)
| (CPU_EXCEPTION_PRIORITY_PENDSV << 16));
*SHPR3 = (CPU_EXCEPTION_PRIORITY_SYSTICK << 24);
}
static void
chx_cpu_sched_lock (void)
{
if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION)
{
#if defined(__ARM_ARCH_6M__)
asm volatile ("cpsid i" : : : "memory");
#else
@@ -219,13 +240,10 @@ chx_cpu_sched_lock (void)
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
#endif
}
}
static void
chx_cpu_sched_unlock (void)
{
if (running->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION)
{
#if defined(__ARM_ARCH_6M__)
asm volatile ("cpsie i" : : : "memory");
#else
@@ -233,85 +251,138 @@ chx_cpu_sched_unlock (void)
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
#endif
}
static void __attribute__ ((naked, used))
involuntary_context_switch (struct chx_thread *tp_next)
{
register struct chx_thread *tp_current asm ("r1");
asm (
"ldr r2, =running\n\t"
"ldr r1, [r2]"
: "=r" (tp_current)
: /* no input */
: "r2");
if (!tp_current)
/* It's idle thread. No need to save registers. */
;
else
{
/* Save registers onto CHX_THREAD struct. */
asm volatile (
"adds %0, #20\n\t"
"stm %0!, {r4, r5, r6, r7}\n\t"
"mov r2, r8\n\t"
"mov r3, r9\n\t"
"mov r4, r10\n\t"
"mov r5, r11\n\t"
"mrs r6, PSP\n\t" /* r13(=SP) in user space. */
"stm %0!, {r2, r3, r4, r5, r6}"
: "=r" (tp_current)
: "0" (tp_current)
/*
* Memory clobber constraint here is not accurate, but this
* works. R7 keeps its value, but having "r7" here prevents
* use of R7 before this asm statement.
*/
: "cc", "r2", "r3", "r4", "r5", "r6", "r7", "memory");
tp_next = chx_running_preempted (tp_next);
}
/* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */
void
asm volatile (
/* Now, r0 points to the thread to be switched. */
/* Put it to *running. */
"ldr r1, =running\n\t"
/* Update running: chx_set_running */
"str r0, [r1]\n\t"
/**/
"adds r0, #20\n\t"
"ldm r0!, {r4, r5, r6, r7}\n\t"
#if defined(__ARM_ARCH_6M__)
"ldm r0!, {r1, r2, r3}\n\t"
"mov r8, r1\n\t"
"mov r9, r2\n\t"
"mov r10, r3\n\t"
"ldm r0!, {r1, r2}\n\t"
"mov r11, r1\n\t"
"msr PSP, r2\n\t"
#else
"ldr r8, [r0], #4\n\t"
"ldr r9, [r0], #4\n\t"
"ldr r10, [r0], #4\n\t"
"ldr r11, [r0], #4\n\t"
"ldr r1, [r0], #4\n\t"
"msr PSP, r1\n\t"
#endif
"movs r0, #0\n\t"
"subs r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0"
: /* no output */ : "r" (tp_next) : "memory");
}
static struct chx_thread *chx_timer_expired (void) __attribute__ ((noinline,used));
static struct chx_thread *chx_recv_irq (uint32_t irq_num) __attribute__ ((noinline,used));
void __attribute__ ((naked))
chx_handle_intr (void)
{
struct chx_pq *p;
register uint32_t irq_num;
register struct chx_thread *tp_next asm ("r0");;
asm volatile ("mrs %0, IPSR\n\t"
"sub %0, #16" /* Exception # - 16 = interrupt number. */
: "=r" (irq_num) : /* no input */ : "memory");
/* Exception # - 16 = interrupt number. */
"subs %0, #16\n\t"
"bpl 0f\n\t"
"bl chx_timer_expired\n\t"
"b 1f\n"
"0:\n\t"
"bl chx_recv_irq\n"
"1:"
: "=r" (tp_next) : /* no input */ : "cc", "memory");
chx_disable_intr (irq_num);
chx_spin_lock (&q_intr.lock);
for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next)
if (p->v == irq_num)
{ /* should be one at most. */
struct chx_px *px = (struct chx_px *)p;
ll_dequeue (p);
chx_wakeup (p);
chx_request_preemption (px->master->prio);
break;
}
chx_spin_unlock (&q_intr.lock);
if (tp_next)
asm volatile (
"b involuntary_context_switch"
: /*no input */ : /* no input */ : "memory");
else
asm volatile (
"movs r0, #0\n\t"
"subs r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0"
: /*no input */ : /* no input */ : "memory");
}
static void
chx_init_arch (struct chx_thread *tp)
{
memset (&tp->tc, 0, sizeof (tp->tc));
}
static void
chx_request_preemption (uint16_t prio)
{
if (running == NULL || (uint16_t)running->prio < prio)
{
*ICSR = (1 << 28);
asm volatile ("" : : : "memory");
}
chx_set_running (tp);
}
/*
* chx_sched: switch to another thread.
*
* There are two cases:
* YIELD=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* YIELD=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*
* For Cortex-M0, this should be AAPCS-compliant function entry, so we
* put "noinline" attribute.
*
* AAPCS: ARM Architecture Procedure Call Standard
*
* Returns:
* >= 1 on wakeup by others, value means ticks remained for sleep.
* 0 on normal wakeup (timer expiration, lock acquirement).
* -1 on cancellation.
*/
static uintptr_t __attribute__ ((naked, noinline))
chx_sched (uint32_t yield)
static uintptr_t
voluntary_context_switch (struct chx_thread *tp_next)
{
register struct chx_thread *tp asm ("r0");
register uintptr_t result asm ("r0");
#if defined(__ARM_ARCH_7M__)
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
asm volatile (
"svc #0"
: "=r" (tp) : "0" (yield): "memory");
"svc #0\n\t"
"add r1, r0, #16\n\t"
"ldr r0, [r1]" /* Get tp->v */
: "=r" (result) : "0" (tp_next): "r1", "memory");
#else
register uint32_t arg_yield asm ("r1");
register struct chx_thread *tp asm ("r1");
/* Build stack data as if it were an exception entry. */
/* Build stack data as if it were an exception entry.
* And set the stack top to have RUNNNING.
*/
/*
* r0: TP scratch
* r0: RUNNING scratch
* r1: 0 scratch
* r2: 0 scratch
* r3: 0 scratch
@@ -320,51 +391,35 @@ chx_sched (uint32_t yield)
* pc: return address (= .L_CONTEXT_SWITCH_FINISH)
* psr: INITIAL_XPSR scratch
*/
asm ("mov r1, lr\n\t"
asm ("mov %0, lr\n\t"
"ldr r2, =.L_CONTEXT_SWITCH_FINISH\n\t"
"mov r3, #128\n\t"
"lsl r3, #17\n\t"
"push {r1, r2, r3}\n\t"
"mov r1, #0\n\t"
"mov r2, r1\n\t"
"mov r3, r1\n\t"
"push {r1, r2, r3}\n\t"
"mov r1, r0\n\t"
"movs r3, #128\n\t"
"lsls r3, #17\n\t"
"push {%0, r2, r3}\n\t"
"movs %0, #0\n\t"
"mov r2, %0\n\t"
"mov r3, %0\n\t"
"push {%0, r2, r3}\n\t"
"ldr r2, =running\n\t"
"ldr r0, [r2]\n\t"
"push {r0, r3}"
: "=r" (tp), "=r" (arg_yield)
: "0" (yield)
: "r2", "r3", "memory");
"ldr %0, [r2]\n\t"
"push {%0, r3}\n\t"
: "=r" (tp)
: /* no input */
: "cc", "r2", "r3", "memory");
/* Save registers onto CHX_THREAD struct. */
asm ("add r0, #20\n\t"
"stm r0!, {r4, r5, r6, r7}\n\t"
asm ("adds r1, #20\n\t"
"stm r1!, {r4, r5, r6, r7}\n\t"
"mov r2, r8\n\t"
"mov r3, r9\n\t"
"mov r4, r10\n\t"
"mov r5, r11\n\t"
"mov r6, sp\n\t"
"stm r0!, {r2, r3, r4, r5, r6}\n\t"
"sub r0, #56"
"stm r1!, {r2, r3, r4, r5, r6}\n\t"
"subs r1, #56"
: /* no output */
: "r" (tp)
: "r2", "r3", "r4", "r5", "r6", "r7", "memory");
if (arg_yield)
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
tp = chx_ready_pop ();
if (tp && tp->flag_sched_rr)
{
chx_spin_lock (&q_timer.lock);
tp = chx_timer_insert (tp, PREEMPTION_USEC);
chx_spin_unlock (&q_timer.lock);
}
: "cc", "r2", "r3", "r4", "r5", "r6", "r7", "memory");
asm volatile (/* Now, r0 points to the thread to be switched. */
/* Put it to *running. */
@@ -385,7 +440,7 @@ chx_sched (uint32_t yield)
/* Normal context switch */
"0:\n\t"
"add r0, #20\n\t"
"adds r0, #20\n\t"
"ldm r0!, {r4, r5, r6, r7}\n\t"
"ldm r0!, {r1, r2, r3}\n\t"
"mov r8, r1\n\t"
@@ -394,15 +449,9 @@ chx_sched (uint32_t yield)
"ldm r0!, {r1, r2}\n\t"
"mov r11, r1\n\t"
"mov sp, r2\n\t"
"sub r0, #45\n\t"
"ldrb r1, [r0]\n\t" /* ->PRIO field. */
"cmp r1, #247\n\t"
"bhi 1f\n\t" /* Leave interrupt disabled if >= 248 */
/**/
/* Unmask interrupts. */
"cpsie i\n"
/**/
"1:\n\t"
"cpsie i\n\t"
/*
0: r0
4: r1
@@ -416,12 +465,12 @@ chx_sched (uint32_t yield)
[28 or 32] <-- pc
*/
"ldr r0, [sp, #28]\n\t"
"lsl r1, r0, #23\n\t"
"lsls r1, r0, #23\n\t"
"bcc 2f\n\t"
/**/
"ldr r2, [sp, #24]\n\t"
"mov r1, #1\n\t"
"orr r2, r1\n\t" /* Ensure Thumb-mode */
"movs r1, #1\n\t"
"orrs r2, r1\n\t" /* Ensure Thumb-mode */
"str r2, [sp, #32]\n\t"
"msr APSR_nzcvq, r0\n\t"
/**/
@@ -434,8 +483,8 @@ chx_sched (uint32_t yield)
"pop {pc}\n"
"2:\n\t"
"ldr r2, [sp, #24]\n\t"
"mov r1, #1\n\t"
"orr r2, r1\n\t" /* Ensure Thumb-mode */
"movs r1, #1\n\t"
"orrs r2, r1\n\t" /* Ensure Thumb-mode */
"str r2, [sp, #28]\n\t"
"msr APSR_nzcvq, r0\n\t"
/**/
@@ -446,17 +495,14 @@ chx_sched (uint32_t yield)
"pop {r0, r1, r2, r3}\n\t"
"add sp, #12\n\t"
"pop {pc}\n\t"
".L_CONTEXT_SWITCH_FINISH:"
: "=r" (tp) /* Return value in R0 */
: "0" (tp)
: "memory");
".L_CONTEXT_SWITCH_FINISH:\n\t"
"adds r0, #16\n\t"
"ldr r0, [r0]" /* Get tp->v */
: "=r" (result) /* Return value in R0 */
: "0" (tp_next)
: "cc", "memory");
#endif
asm volatile ("bx lr"
: "=r" (tp)
: "0" (tp->v)
: "memory");
return (uintptr_t)tp;
return result;
}
extern void cause_link_time_error_unexpected_size_of_struct_chx_thread (void);
@@ -491,177 +537,19 @@ chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
return tp;
}
/*
* Lower layer architecture specific exception handling entries.
*
*/
void __attribute__ ((naked))
preempt (void)
{
register struct chx_thread *tp asm ("r0");
register struct chx_thread *cur asm ("r1");
asm volatile (
#if defined(__ARM_ARCH_6M__)
"cpsid i\n\t"
#else
"msr BASEPRI, r0\n\t"
#endif
"ldr r2, =running\n\t"
"ldr r0, [r2]\n\t"
"mov r1, r0"
: "=r" (tp), "=r" (cur)
: "0" (CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED)
: "r2");
if (!cur)
/* It's idle thread. It's ok to clobber registers. */
;
else
{
/* Save registers onto CHX_THREAD struct. */
asm volatile (
"add %0, #20\n\t"
"stm %0!, {r4, r5, r6, r7}\n\t"
"mov r2, r8\n\t"
"mov r3, r9\n\t"
"mov r4, r10\n\t"
"mov r5, r11\n\t"
"mrs r6, PSP\n\t" /* r13(=SP) in user space. */
"stm %0!, {r2, r3, r4, r5, r6}"
: "=r" (cur)
: "0" (cur)
/*
* Memory clobber constraint here is not accurate, but this
* works. R7 keeps its value, but having "r7" here prevents
* use of R7 before this asm statement.
*/
: "r2", "r3", "r4", "r5", "r6", "r7", "memory");
if (tp)
{
if (tp->flag_sched_rr)
{
if (tp->state == THREAD_RUNNING)
{
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
/*
* It may be THREAD_READY after chx_timer_expired.
* Then, do nothing.
*/
}
else
chx_ready_push (tp);
running = NULL;
}
}
/* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */
tp = chx_ready_pop ();
if (tp && tp->flag_sched_rr)
{
chx_spin_lock (&q_timer.lock);
tp = chx_timer_insert (tp, PREEMPTION_USEC);
chx_spin_unlock (&q_timer.lock);
}
asm volatile (
".L_CONTEXT_SWITCH:\n\t"
/* Now, r0 points to the thread to be switched. */
/* Put it to *running. */
"ldr r1, =running\n\t"
/* Update running. */
"str r0, [r1]\n\t"
#if defined(__ARM_ARCH_6M__)
"cmp r0, #0\n\t"
"beq 1f\n\t"
#else
"cbz r0, 1f\n\t"
#endif
/**/
"add r0, #20\n\t"
"ldm r0!, {r4, r5, r6, r7}\n\t"
#if defined(__ARM_ARCH_6M__)
"ldm r0!, {r1, r2, r3}\n\t"
"mov r8, r1\n\t"
"mov r9, r2\n\t"
"mov r10, r3\n\t"
"ldm r0!, {r1, r2}\n\t"
"mov r11, r1\n\t"
"msr PSP, r2\n\t"
#else
"ldr r8, [r0], #4\n\t"
"ldr r9, [r0], #4\n\t"
"ldr r10, [r0], #4\n\t"
"ldr r11, [r0], #4\n\t"
"ldr r1, [r0], #4\n\t"
"msr PSP, r1\n\t"
#endif
"sub r0, #45\n\t"
"ldrb r1, [r0]\n\t" /* ->PRIO field. */
"mov r0, #0\n\t"
"cmp r1, #247\n\t"
"bhi 0f\n\t" /* Leave interrupt disabled if >= 248 */
/**/
/* Unmask interrupts. */
#if defined(__ARM_ARCH_6M__)
"cpsie i\n"
#else
"msr BASEPRI, r0\n"
#endif
/**/
"0:\n\t"
"sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0\n"
"1:\n\t"
/* Spawn an IDLE thread. */
"ldr r0, =__main_stack_end__-32\n\t"
"msr PSP, r0\n\t"
"mov r1, #0\n\t"
"mov r2, #0\n\t"
"mov r3, #0\n\t"
"stm r0!, {r1, r2, r3}\n\t"
"stm r0!, {r1, r2, r3}\n\t"
"ldr r1, =chx_idle\n\t" /* PC = idle */
"mov r2, #0x010\n\t"
"lsl r2, r2, #20\n\t" /* xPSR = T-flag set (Thumb) */
"stm r0!, {r1, r2}\n\t"
/**/
/* Unmask interrupts. */
"mov r0, #0\n\t"
#if defined(__ARM_ARCH_6M__)
"cpsie i\n\t"
#else
"msr BASEPRI, r0\n"
#endif
/**/
"sub r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0"
: /* no output */ : "r" (tp) : "memory");
}
#if defined(__ARM_ARCH_7M__)
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
/*
* System call: switch to another thread.
* There are two cases:
* ORIG_R0=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* ORIG_R0=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*/
void __attribute__ ((naked))
svc (void)
{
register struct chx_thread *tp asm ("r0");
register uint32_t orig_r0 asm ("r1");
register uint32_t tp_next asm ("r0");
asm ("ldr r1, =running\n\t"
"ldr r0, [r1]\n\t"
"add r1, r0, #20\n\t"
"ldr r1, [r1]\n\t"
"adds r1, #20\n\t"
/* Save registers onto CHX_THREAD struct. */
"stm r1!, {r4, r5, r6, r7}\n\t"
"mov r2, r8\n\t"
@@ -670,30 +558,52 @@ svc (void)
"mov r5, r11\n\t"
"mrs r6, PSP\n\t" /* r13(=SP) in user space. */
"stm r1!, {r2, r3, r4, r5, r6}\n\t"
"ldr r1, [r6]\n\t"
"str r0, [r6]"
: "=r" (tp), "=r" (orig_r0)
"ldr r0, [r6]\n\t"
"subs r1, #56\n\t"
"str r1, [r6]"
: "=r" (tp_next)
: /* no input */
: "r2", "r3", "r4", "r5", "r6", "memory");
if (orig_r0) /* yield */
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
running = NULL;
}
tp = chx_ready_pop ();
if (tp && tp->flag_sched_rr)
{
chx_spin_lock (&q_timer.lock);
chx_timer_insert (tp, PREEMPTION_USEC);
chx_spin_unlock (&q_timer.lock);
}
: "cc", "r1", "r2", "r3", "r4", "r5", "r6", "memory");
asm volatile (
"b .L_CONTEXT_SWITCH"
: /* no output */ : "r" (tp) : "memory");
/* Now, r0 points to the thread to be switched. */
/* Put it to *running. */
"ldr r1, =running\n\t"
/* Update running: chx_set_running */
"str r0, [r1]\n\t"
"cbz r0, 1f\n\t"
/**/
"adds r0, #20\n\t"
"ldm r0!, {r4, r5, r6, r7}\n\t"
"ldr r8, [r0], #4\n\t"
"ldr r9, [r0], #4\n\t"
"ldr r10, [r0], #4\n\t"
"ldr r11, [r0], #4\n\t"
"ldr r1, [r0], #4\n\t"
"msr PSP, r1\n\t"
/* Unmask interrupts. */
"movs r0, #0\n\t"
"msr BASEPRI, r0\n\t"
"subs r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0\n"
"1:\n\t"
/* Spawn an IDLE thread. */
"ldr r0, =__main_stack_end__-32\n\t"
"msr PSP, r0\n\t"
"movs r1, #0\n\t"
"movs r2, #0\n\t"
"movs r3, #0\n\t"
"stm r0!, {r1, r2, r3}\n\t"
"stm r0!, {r1, r2, r3}\n\t"
"ldr r1, =chx_idle\n\t" /* PC = idle */
"movs r2, #0x010\n\t"
"lsls r2, r2, #20\n\t" /* xPSR = T-flag set (Thumb) */
"stm r0!, {r1, r2}\n\t"
/* Unmask interrupts. */
"movs r0, #0\n\t"
"msr BASEPRI, r0\n"
"subs r0, #3\n\t" /* EXC_RETURN to a thread with PSP */
"bx r0"
: /* no output */ : "r" (tp_next) : "memory");
}
#endif

View File

@@ -10,3 +10,5 @@ struct tcontext {
};
typedef struct tcontext tcontext_t;
#define CHOPSTX_THREAD_SIZE 64

View File

@@ -2,7 +2,7 @@
* chopstx-gnu-linux.c - Threads and only threads: Arch specific code
* for GNU/Linux emulation
*
* Copyright (C) 2017, 2018 Flying Stone Technology
* Copyright (C) 2017, 2018, 2019, 2021 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -23,7 +23,17 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
/*
* NOTE: This code is not portable. It's intended for GNU/Linux.
*
* This implementation depends on the feature of SA_SIGINFO of Linux,
* which signal handler takes three arguments. Also, it depends on
* GNU C library for how to use the makecontext function on 64-bit
* machine.
*
*/
@@ -32,6 +42,21 @@
#include <signal.h>
#include <sys/time.h>
static struct chx_thread *running;
static struct chx_thread *
chx_running (void)
{
return running;
}
static void
chx_set_running (struct chx_thread *r)
{
running = r;
}
/* Data Memory Barrier. */
static void
chx_dmb (void)
@@ -39,10 +64,12 @@ chx_dmb (void)
}
static void preempted_context_switch (struct chx_thread *tp_next);
static sigset_t ss_cur;
static void
chx_systick_reset (void)
chx_systick_init_arch (void)
{
const struct itimerval it = { {0, 0}, {0, 0} };
@@ -76,6 +103,11 @@ usec_to_ticks (uint32_t usec)
return usec * MHZ;
}
static uint32_t
ticks_to_usec (uint32_t ticks)
{
return ticks / MHZ;
}
static void
chx_enable_intr (uint8_t irq_num)
@@ -89,10 +121,13 @@ chx_clr_intr (uint8_t irq_num)
(void)irq_num;
}
static void
static int
chx_disable_intr (uint8_t irq_num)
{
int already_disabled = sigismember (&ss_cur, irq_num);
sigaddset (&ss_cur, irq_num);
return already_disabled;
}
static void
@@ -102,7 +137,7 @@ chx_set_intr_prio (uint8_t n)
}
static void
chx_prio_init (void)
chx_interrupt_controller_init (void)
{
}
@@ -121,38 +156,50 @@ chx_cpu_sched_unlock (void)
pthread_sigmask (SIG_SETMASK, &ss_cur, NULL);
}
static void
idle (void)
/* NOTE: Called holding the cpu_sched_lock. */
static struct chx_thread *
chx_idle (void)
{
for (;;)
pause ();
struct chx_thread *tp_next = NULL;
while (tp_next == NULL)
{
int sig;
sigset_t set;
sigfillset (&set);
if (sigwait (&set, &sig))
continue;
if (sig == SIGALRM)
tp_next = chx_timer_expired ();
else
{
tp_next = chx_recv_irq (sig);
/* Exit when there is no waiter and it's INT or TERM. */
if (tp_next == NULL
&& (sig == SIGINT || sig == SIGTERM))
exit (1);
}
}
return tp_next;
}
void
chx_handle_intr (uint32_t irq_num)
{
struct chx_pq *p;
struct chx_thread *tp_next;
chx_disable_intr (irq_num);
chx_spin_lock (&q_intr.lock);
for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next)
if (p->v == irq_num)
{ /* should be one at most. */
struct chx_px *px = (struct chx_px *)p;
ll_dequeue (p);
chx_wakeup (p);
chx_spin_unlock (&q_intr.lock);
chx_request_preemption (px->master->prio);
tp_next = chx_recv_irq (irq_num);
if (!tp_next)
return;
}
chx_spin_unlock (&q_intr.lock);
tp_next = chx_running_preempted (tp_next);
preempted_context_switch (tp_next);
}
static ucontext_t idle_tc;
static char idle_stack[4096];
struct chx_thread main_thread;
void
@@ -170,11 +217,17 @@ chx_sigmask (ucontext_t *uc)
static void
sigalrm_handler (int sig, siginfo_t *siginfo, void *arg)
{
extern void chx_timer_expired (void);
struct chx_thread *tp_next;
ucontext_t *uc = arg;
(void)sig;
(void)siginfo;
chx_timer_expired ();
tp_next = chx_timer_expired ();
if (tp_next)
{
tp_next = chx_running_preempted (tp_next);
preempted_context_switch (tp_next);
}
chx_sigmask (uc);
}
@@ -190,57 +243,15 @@ chx_init_arch (struct chx_thread *tp)
sa.sa_flags = SA_SIGINFO|SA_RESTART;
sigaction (SIGALRM, &sa, NULL);
getcontext (&idle_tc);
idle_tc.uc_stack.ss_sp = idle_stack;
idle_tc.uc_stack.ss_size = sizeof (idle_stack);
idle_tc.uc_link = NULL;
makecontext (&idle_tc, idle, 0);
getcontext (&tp->tc);
chx_set_running (tp);
}
static void
chx_request_preemption (uint16_t prio)
preempted_context_switch (struct chx_thread *tp_next)
{
struct chx_thread *tp, *tp_prev;
ucontext_t *tcp;
struct chx_thread *tp_prev = chx_running ();
if (running && (uint16_t)running->prio >= prio)
return;
/* Change the context to another thread with higher priority. */
tp = tp_prev = running;
if (tp)
{
if (tp->flag_sched_rr)
{
if (tp->state == THREAD_RUNNING)
{
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
}
else
chx_ready_push (tp);
running = NULL;
}
tp = running = chx_ready_pop ();
if (tp)
{
tcp = &tp->tc;
if (tp->flag_sched_rr)
{
chx_spin_lock (&q_timer.lock);
tp = chx_timer_insert (tp, PREEMPTION_USEC);
chx_spin_unlock (&q_timer.lock);
}
}
else
tcp = &idle_tc;
if (tp_prev)
{
/*
* The swapcontext implementation may reset sigmask in the
* middle of its execution, unfortunately. It is best if
@@ -257,72 +268,45 @@ chx_request_preemption (uint16_t prio)
* of the thread, and the condition of chx_sched function which
* mandates holding cpu_sched_lock.
*/
swapcontext (&tp_prev->tc, tcp);
}
else if (tp)
{
setcontext (tcp);
}
chx_set_running (tp_next);
swapcontext (&tp_prev->tc, &tp_next->tc);
}
/*
* chx_sched: switch to another thread.
*
* There are two cases:
* YIELD=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* YIELD=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*
* Returns:
* 1 on wakeup by others.
* 0 on normal wakeup.
* -1 on cancellation.
*/
static uintptr_t
chx_sched (uint32_t yield)
voluntary_context_switch (struct chx_thread *tp_next)
{
struct chx_thread *tp, *tp_prev;
uintptr_t v;
ucontext_t *tcp;
struct chx_thread *tp;
struct chx_thread *tp_prev;
tp = tp_prev = running;
if (yield)
tp_prev = chx_running ();
if (!tp_next)
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
}
running = tp = chx_ready_pop ();
if (tp)
{
v = tp->v;
if (tp->flag_sched_rr)
{
chx_spin_lock (&q_timer.lock);
tp = chx_timer_insert (tp, PREEMPTION_USEC);
chx_spin_unlock (&q_timer.lock);
}
tcp = &tp->tc;
chx_set_running (NULL);
tp_next = chx_idle ();
chx_set_running (tp_next);
if (tp_prev != tp_next)
swapcontext (&tp_prev->tc, &tp_next->tc);
}
else
{
v = 0;
tcp = &idle_tc;
chx_set_running (tp_next);
swapcontext (&tp_prev->tc, &tp_next->tc);
}
swapcontext (&tp_prev->tc, tcp);
chx_cpu_sched_unlock ();
return v;
tp = chx_running ();
return tp->v;
}
static void __attribute__((__noreturn__))
chx_thread_start (voidfunc thread_entry, void *arg)
{
void *ret;
chx_cpu_sched_unlock ();
thread_entry (arg);
chopstx_exit (0);
ret = thread_entry (arg);
chopstx_exit (ret);
}
static struct chx_thread *
@@ -339,13 +323,22 @@ chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
* signal blocked. The sigmask will be cleared in chx_thread_start.
*/
chx_cpu_sched_lock ();
memset (tp, 0, sizeof (struct chx_thread));
getcontext (&tp->tc);
tp->tc.uc_stack.ss_sp = (void *)stack_addr;
tp->tc.uc_stack.ss_size = stack_size;
tp->tc.uc_link = NULL;
/*
* makecontext is hard to use in a portable way, actually.
*
* On 64-bit machine, according to the standard, it should be coded
* to specify int (== 32-bit for LP64 machine) arguments that follow
* ARGC, so it is not that correct to specify two 64-bit arguments
* here. However, GNU C library allows this.
*/
makecontext (&tp->tc, (void (*)(void))chx_thread_start,
4, thread_entry, arg);
2, thread_entry, arg);
chx_cpu_sched_unlock ();
return tp;
}

684
chopstx-riscv32.c Normal file
View File

@@ -0,0 +1,684 @@
/*
* chopstx-riscv32.c - Threads and only threads: Arch specific code
* for RISC-V 32 IMAC (Bumblebee core)
*
* Copyright (C) 2019, 2021
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
/*
* Define Bumblebee specific CSRs
*/
asm (
".equ msubm,0x7c4\n\t" /* No use (not needed to care about) */
".equ mtvt,0x307\n\t"
".equ mtvt2,0x7ec\n\t"
);
/* Data Memory Barrier. */
static void
chx_dmb (void)
{
asm volatile ("fence" : : : "memory");
}
/* No saved registers on the stack. */
/*
* Constants for RISC-V.
*/
#define REG_PC 0
#define REG_RA 1
#define REG_SP 2
#define REG_GP 3
#define REG_TP 4
#define REG_A0 10
#define MACHINE_STATUS_INIT 0x00001880 /* Machine-mode, interrupt enabled. */
/*
* We keep the TP register to the thread context address.
* Also, it is kept at the MSCRATCH register.
*/
static struct chx_thread *
chx_running (void)
{
uint32_t r;
asm ( "mv %0,tp" : "=r" (r));
if (r == 0)
return NULL;
return (struct chx_thread *)(r - 20);
}
static void
chx_set_running (struct chx_thread *tp)
{
asm ( "addi tp,%0,20\n\t"
"csrw mscratch,tp" : /* no output */ : "r" (tp) : "memory");
}
/*
* Lower layer architecture specific functions.
*
* system tick and interrupt
*/
/*
* System tick
*
* SYSTICK timer model is decreasing counter, where counter value 0
* means the timer stop.
*
* Since RISC-V MTIME is ever increasing counter, we need a bit of
* glue code here. Fortunately, we have MSTOP register for Bumblebee
* core, so, we can use the register to stop the counter. (If no such
* register stopping MTIME, we could put possible largest value in
* MTIMECMP, so that the timer won't be fired.)
*/
/* TIMER registers. */
struct TIMER {
volatile uint32_t mtime_lo;
volatile uint32_t mtime_hi;
volatile uint32_t mtimecmp_lo;
volatile uint32_t mtimecmp_hi;
uint32_t rsv0[1018];
volatile uint8_t mstop;
uint8_t rsv1[3];
volatile uint8_t msip;
uint8_t rsv2[3];
} __attribute__ ((packed));
static struct TIMER *const TIMER = (struct TIMER *)0xD1000000;
/* In Chopstx, we only use the lower 32-bit of the timer. */
static void
chx_systick_init_arch (void)
{
TIMER->mstop |= 1;
TIMER->mtime_hi = TIMER->mtime_lo = 0;
TIMER->mtimecmp_lo = 0xffffffff;
TIMER->mtimecmp_hi = 0;
}
/*
* @ticks: 0 means to stop the timer, otherwise update the timer
*/
static void
chx_systick_reload (uint32_t ticks)
{
if (!ticks)
{
TIMER->mstop |= 1;
TIMER->mtimecmp_lo = 0xffffffff;
}
else
{
TIMER->mtime_lo = 0;
TIMER->mtimecmp_lo = ticks;
if ((TIMER->mstop & 1))
TIMER->mstop &= ~1;
}
}
static uint32_t
chx_systick_get (void)
{
int stopped = (TIMER->mstop & 1);
if (stopped)
return 0;
else
{
uint32_t now = TIMER->mtime_lo;
if (TIMER->mtimecmp_lo <= now)
return 0;
return TIMER->mtimecmp_lo - now;
}
}
/* TIMER runs at 1/4 of core clock. */
static uint32_t
usec_to_ticks (uint32_t usec)
{
return usec * (MHZ/4);
}
static uint32_t
ticks_to_usec (uint32_t ticks)
{
return ticks / (MHZ/4);
}
/*
* Interrupt Handling of (E)CLIC
*/
/* CLIC registers. */
struct CLIC {
volatile uint8_t cfg;
uint8_t rsv[3];
volatile uint32_t info;
volatile uint8_t uth;
volatile uint8_t sth;
volatile uint8_t hth;
volatile uint8_t mth;
} __attribute__ ((packed));
static struct CLIC *const CLIC = (struct CLIC *)0xD2000000;
struct CLIC_INT {
volatile uint8_t ip;
volatile uint8_t ie;
volatile uint8_t attr;
volatile uint8_t ctl;
} __attribute__ ((packed));
static struct CLIC_INT *const CLIC_INT = (struct CLIC_INT *)0xD2001000;
static void
chx_enable_intr (uint8_t irq_num)
{
CLIC_INT[irq_num].ie |= 1;
}
static void
chx_clr_intr (uint8_t irq_num)
{
/* Clear pending interrupt is done automatically by the hardware. */
(void)irq_num;
}
static int
chx_disable_intr (uint8_t irq_num)
{
int already_disabled = !(CLIC_INT[irq_num].ie & 1);
CLIC_INT[irq_num].ie &= ~1;
return already_disabled;
}
static void
chx_set_intr_prio (uint8_t irq_num)
{
CLIC_INT[irq_num].attr = 0xc0; /* Level triggered, SHV=0 */
/* Note: SHV: Selective Hardware Vectoring: off (use common routine). */
CLIC_INT[irq_num].ctl = 0xff;
}
#define SWINT_IRQ 3
#define TIMER_IRQ 7
#define MEMERR_IRQ 17 /* Why on earth it's IRQ??? */
static void chx_handle_intr (void);
static void exception_handler (void);
/* Alignment to 64 is needed to enable ECLIC mode. */
static void __attribute__ ((naked,aligned(64)))
exception_handler (void)
{
asm volatile (
"0: j 0b"
: /* no output */);
}
static void __attribute__ ((naked))
memory_error (void)
{
asm volatile (
"0: j 0b"
: /* no output */);
}
typedef void (*handler)(void);
/* Not used (because we always disable SHV for all interrupts),
* Just in case, if some interrupt uses SHV=1.
*/
static const handler vector_table[] __attribute__ ((aligned(512))) = {
0, 0, 0, chx_handle_intr,
0, 0, 0, chx_handle_intr,
0, 0, 0, 0,
0, 0, 0, 0,
0, memory_error, 0, 0,
};
static void
chx_interrupt_controller_init (void)
{
asm volatile (
"csrw mtvt,%0"
: /* no output */ : "r" (vector_table) : "memory" );
asm volatile (
"csrw mtvt2,%0\n\t"
"csrsi mtvt2,1"
: /* no output */ : "r" (chx_handle_intr) : "memory");
asm volatile (
"csrw mtvec,%0\n\t"
"csrsi mtvec,3" /* Enable ECLC mode */
: /* no output */ : "r" (exception_handler) : "memory" );
CLIC->cfg &= 0xe1; /* NLBITS = 0 */
CLIC->mth = 0;
/* In Bumblebee core, timer interrupt is also handled by CLIC. */
chx_set_intr_prio (SWINT_IRQ);
chx_enable_intr (SWINT_IRQ);
chx_set_intr_prio (TIMER_IRQ);
chx_enable_intr (TIMER_IRQ);
chx_set_intr_prio (MEMERR_IRQ);
chx_enable_intr (MEMERR_IRQ);
}
/* Just for testing TIMER and ECLIC. No real use for now. */
static void
chx_sw_int (int go)
{
if (go)
TIMER->msip |= 1;
else
TIMER->msip &= ~1;
}
static void
chx_cpu_sched_lock (void)
{
asm volatile ("csrci mstatus,8" : : : "memory" );
}
static void
chx_cpu_sched_unlock (void)
{
asm volatile ("csrsi mstatus,8" : : : "memory" );
}
static void
chx_init_arch (struct chx_thread *tp)
{
memset (&tp->tc, 0, sizeof (tp->tc));
chx_set_running (tp);
}
#define SAVE_CALLEE_SAVE_REGISTERS \
"# Save callee save registers\n\t" \
"sw s0,32(sp)\n\t" \
"sw s1,36(sp)\n\t" \
"sw s2,72(sp)\n\t" \
"sw s3,76(sp)\n\t" \
"sw s4,80(sp)\n\t" \
"sw s5,84(sp)\n\t" \
"sw s6,88(sp)\n\t" \
"sw s7,92(sp)\n\t" \
"sw s8,96(sp)\n\t" \
"sw s9,100(sp)\n\t" \
"sw s10,104(sp)\n\t" \
"sw s11,108(sp)\n\t"
#define RESTORE_CALLEE_SAVE_REGISTERS \
"# Restore callee save registers\n\t" \
"lw s0,32(sp)\n\t" \
"lw s1,36(sp)\n\t" \
"lw s2,72(sp)\n\t" \
"lw s3,76(sp)\n\t" \
"lw s4,80(sp)\n\t" \
"lw s5,84(sp)\n\t" \
"lw s6,88(sp)\n\t" \
"lw s7,92(sp)\n\t" \
"lw s8,96(sp)\n\t" \
"lw s9,100(sp)\n\t" \
"lw s10,104(sp)\n\t" \
"lw s11,108(sp)\n\t"
#define SAVE_OTHER_REGISTERS \
"# Save other registers\n\t" \
"sw ra,4(sp)\n\t" \
"sw gp,12(sp)\n\t" \
"sw t0,20(sp)\n\t" \
"sw t1,24(sp)\n\t" \
"sw t2,28(sp)\n\t" \
"sw a0,40(sp)\n\t" \
"sw a1,44(sp)\n\t" \
"sw a2,48(sp)\n\t" \
"sw a3,52(sp)\n\t" \
"sw a4,56(sp)\n\t" \
"sw a5,60(sp)\n\t" \
"sw a6,64(sp)\n\t" \
"sw a7,68(sp)\n\t" \
"sw t3,112(sp)\n\t" \
"sw t4,116(sp)\n\t" \
"sw t5,120(sp)\n\t" \
"sw t6,124(sp)\n\t"
#define RESTORE_OTHER_REGISTERS \
"# Restore other registers\n\t" \
"lw ra,4(sp)\n\t" \
"lw gp,12(sp)\n\t" \
"lw t0,20(sp)\n\t" \
"lw t1,24(sp)\n\t" \
"lw t2,28(sp)\n\t" \
"lw a0,40(sp)\n\t" \
"lw a1,44(sp)\n\t" \
"lw a2,48(sp)\n\t" \
"lw a3,52(sp)\n\t" \
"lw a4,56(sp)\n\t" \
"lw a5,60(sp)\n\t" \
"lw a6,64(sp)\n\t" \
"lw a7,68(sp)\n\t" \
"lw t3,112(sp)\n\t" \
"lw t4,116(sp)\n\t" \
"lw t5,120(sp)\n\t" \
"lw t6,124(sp)\n\t"
/*
* MSTATUS register:
* 31: SD
* 16-15: XS (Extra Unit state)
* 14-13: FS (Floating-point Unit state)
* 12-11: MPP (Previous Privilege)
* 7: MPIE (Previous Interrupt Enable flag)
* 3: MIE (Interrupt Enable flag)
*/
/*
* MSUBM register:
* 9-8 PTYP (Previous Type-of-execution)
* 7-6 TYP (Currunt Type-of-execution)
* 0: Normal, 1: Interrupt, 2: Excep, 3: NMI
*
* No need to store PTYP field of MSUBM in MACHINE_STATUS.
* No need to setup MSUBM for PTYP.
* Since it's always 0 when this is called.
*
* Save MPP..MPIE in MACHINE_STATUS in the thread context.
*/
#define SETUP_MSTATUS_FROM_MACHINE_STATUS \
"lw a0,128(sp)\n\t" /* MPP..MPIE is in MACHINE_STATUS */ \
"csrr a1,mstatus\n\t" \
"srli a1,a1,13\n\t" \
"slli a1,a1,13\n\t" /* SD, XS, and FS bits from MSTATUS */ \
"or a0,a0,a1\n\t" \
"csrw mstatus,a0\n\t" /* Note: keep MIE=0 */
/* It is good if ISA has a single instruction for this operation. */
#define CATCH_AN_INTERRUPT_SYNCHRONOUSLY \
"mv a0,zero\n" \
"0:\n\t" \
/* \
* Interrupt is masked here, and it executes the WFI \
* instruction. When an interrupt occurs, the core is waken \
* up (even if it is masked). \
*/ \
"csrci mstatus,8\n\t" /* Interrupt should be masked already. */ \
/* Nevertheless, make sure it's masked. */ \
"wfi\n\t" \
/* \
* It is good if MCAUSE were updated here, but it is only \
* updated when the control goes into the interrupt service, \
* in a step of the interrupt handling steps of the core. So, \
* we let it go to chx_handle_intr, by unmasking. \
*/ \
"csrsi mstatus,8\n\t" /* Unmask interrupts to catch one. */ \
/* Just before this line, it is interrupted. */ \
/* And interrupt is masked and a0 is set. */ \
"beqz a0,0b\n\t" /* Just in case if not, loop. */
/*
* The idle function.
*
* NOTE: In this function, interrupt is masked (MIE=0) and interrupt
* is synchronously handled.
*/
static struct chx_thread * __attribute__ ((used))
chx_idle (void)
{
extern void chx_prepare_sleep_mode (void);
register struct chx_thread *tp_next asm ("a0") = NULL;
while (tp_next == NULL)
{
register uint32_t irq_num asm ("a0");
chx_prepare_sleep_mode (); /* MCU specific sleep setup */
asm volatile (
CATCH_AN_INTERRUPT_SYNCHRONOUSLY
/*
* In chx_handle_intr, a0 is set by the value of MCAUSE.
*/
"slli a0,a0,20\n\t"
"srli a0,a0,20" /* Take lower 12-bit of MCAUSE */
: "=r" (irq_num));
/* Note: here, interrupt is masked again. */
if (irq_num == SWINT_IRQ)
chx_sw_int (0);
else if (irq_num == TIMER_IRQ)
tp_next = chx_timer_expired ();
else if (irq_num == MEMERR_IRQ)
memory_error ();
else
tp_next = chx_recv_irq (irq_num);
}
return tp_next;
}
static uintptr_t
voluntary_context_switch (struct chx_thread *tp_next)
{
register uintptr_t result asm ("a0");
asm volatile (
/* Here, %0 (a0) points to pointer (struct chx_thread *) to be
* switched. We get the thread context pointer adding the
* offset.
*/
"# Voluntary context switch\n\t"
"sw sp,8(tp)\n\t"
"mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */
"sw zero,0(sp)\n\t"
SAVE_CALLEE_SAVE_REGISTERS
"# Check if going to IDLE function\n\t"
"bnez %0,0f\n\t"
/*
* NOTE: Here, for running chx_idle, we can use
* __main_stack_end__, because neither any threads, nor
* interrupt context use that. Using __main_stack_end__, we
* can minimize stack memory usage of each thread.
*/
"# Call the IDLE function, interrupt masked.\n\t"
"mv tp,zero\n\t"
"csrw mscratch,tp\n\t" /* chx_set_running */
"la sp,__main_stack_end__\n\t"
"call chx_idle\n"
".L_V_CONTEXT_SWITCH_BEGIN:\n"
"0:\n\t"
"addi %0,%0,20\n\t"
"mv sp,%0\n\t"
RESTORE_CALLEE_SAVE_REGISTERS
/**/
"csrw mscratch,sp\n\t" /* chx_set_running */
"lw a0,0(sp)\n\t"
"beqz a0,1f\n"
".L_RETURN_TO_PREEMPTED_THREAD:\n\t"
"csrw mepc,a0\n\t"
SETUP_MSTATUS_FROM_MACHINE_STATUS
RESTORE_OTHER_REGISTERS
"lw tp,16(sp)\n\t" /* Application is free to other use of TP */
"lw sp,8(sp)\n\t"
"mret\n"
"1:\n\t"
"lw a0,-4(sp)\n\t" /* Get the result value */
"mv tp,sp\n\t"
"lw sp,8(sp)\n\t"
"csrsi mstatus,8\n" /* Unmask interrupts. */
".L_V_CONTEXT_SWITCH_FINISH:"
: "=r" (result)
: "0" (tp_next)
: "ra", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
"a1", "a2", "a3", "a4", "a5", "a6", "a7",
"memory");
return result;
}
extern void cause_link_time_error_unexpected_size_of_struct_chx_thread (void);
static struct chx_thread *
chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
voidfunc thread_entry, void *arg)
{
struct chx_thread *tp;
void *stack;
register uint32_t gp;
asm ( "mv %0,gp" : "=r" (gp));
if (CHOPSTX_THREAD_SIZE != sizeof (struct chx_thread))
cause_link_time_error_unexpected_size_of_struct_chx_thread ();
if (stack_size < sizeof (struct chx_thread))
chx_fatal (CHOPSTX_ERR_THREAD_CREATE);
stack = (void *)(stack_addr + stack_size - sizeof (struct chx_thread));
tp = (struct chx_thread *)stack;
memset (&tp->tc, 0, sizeof (tp->tc));
tp->tc.reg[REG_A0] = (uint32_t)arg;
tp->tc.reg[REG_RA] = (uint32_t)chopstx_exit;
tp->tc.reg[REG_PC] = (uint32_t)thread_entry;
tp->tc.reg[REG_GP] = gp;
tp->tc.reg[REG_TP] = (uint32_t)&tp->tc;
tp->tc.reg[REG_SP] = (uint32_t)stack;
tp->tc.machine_status = MACHINE_STATUS_INIT;
return tp;
}
/*
* Note: Examine the assembler output carefully, because it has
* ((naked)) attribute
*/
static void __attribute__ ((naked,aligned(4)))
chx_handle_intr (void)
{
struct chx_thread *tp_next;
uint32_t irq_num;
/*
* stack setup to __main_stack_end__
* save registers.
*/
asm volatile (
"csrrw sp,mscratch,sp\n\t" /* SP to MSCRATCH, thread pointer into SP */
"# Check if it is IDLE\n\t"
"bnez sp,0f\n\t"
/**/
"csrrw sp,mscratch,sp\n\t" /* Recover MSCRATCH, the thread pointer */
"li a0,0x80\n\t"
"csrrc x0,mstatus,a0\n\t" /* Clear MPIE bit to mask interrupt */
"csrr a0,mcause\n\t"
"mret\n"
"0:\n\t"
"sw tp,16(sp)\n\t" /* Application is free to other use of TP */
SAVE_OTHER_REGISTERS
"csrr a0,mepc\n\t"
"sw a0,0(sp)\n\t"
/**/
"mv tp,sp\n\t" /* TP is now the thread pointer */
"csrrw sp,mscratch,sp\n\t" /* TP to MSCRATCH, SP_old into SP */
"sw sp,8(tp)\n\t"
"la sp,__main_stack_end__");
/*
* The stack at __main_stack_end__ is shared data between chx_idle
* and this handler. When it comes here, there is no active chx_idle,
* so, it is safe to use the stack.
*
* Note that when there are multiple cores, we need this stack for
* each core.
*/
asm ( "csrr %0,mcause\n\t"
"slli %0,%0,20\n\t"
"srli %0,%0,20" /* Take lower 12-bit of MCAUSE */
: "=r" (irq_num));
tp_next = NULL;
if (irq_num == SWINT_IRQ)
chx_sw_int (0);
else if (irq_num == TIMER_IRQ)
tp_next = chx_timer_expired ();
else if (irq_num == MEMERR_IRQ)
memory_error ();
else
tp_next = chx_recv_irq (irq_num);
if (!tp_next)
asm volatile (
"mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */
RESTORE_OTHER_REGISTERS
"lw tp,16(sp)\n\t" /* Application is free to other use of TP */
"lw sp,8(sp)\n\t"
"mret");
tp_next = chx_running_preempted (tp_next);
asm volatile (
"# Involuntary context switch\n\t"
"mv sp,tp\n\t" /* Using SP, we can use C.SWSP instruction */
SAVE_CALLEE_SAVE_REGISTERS
/*
* MACHINE_STATUS = (MSTATUS & 0x00001f80)
*/
"csrr a1,mstatus\n\t"
"andi a1,a1,-9\n\t" /* Clear MIE (bit3) */
"slli a1,a1,19\n\t" /* Clear bit31 to bit13 */
"srli a1,a1,19\n\t" /* MPP..MPIE from MSTATUS */
"sw a1,128(sp)\n"
".L_IV_CONTEXT_SWITCH_BEGIN:\n\t"
"addi %0,%0,20\n\t"
"mv sp,%0\n\t"
RESTORE_CALLEE_SAVE_REGISTERS
/**/
"csrw mscratch,sp\n\t" /* chx_set_running */
"lw a0,0(sp)\n\t"
"bnez a0,.L_RETURN_TO_PREEMPTED_THREAD\n\t"
/**/
"lw a0,-4(sp)\n\t" /* Get the result value */
"mv tp,sp\n\t"
"lw sp,8(sp)\n\t"
"la a1,.L_V_CONTEXT_SWITCH_FINISH\n\t"
"csrw mepc,a1\n\t"
"li a1,0x188\n\t" /* Set MPIE and MPP bits */
"slli a1,a1,4\n\t"
"csrrs x0,mstatus,a1\n\t"/* Prev: Machine mode, enable interrupt */
"mret" /* Return to Prev */
: /* no output */ : "r" (tp_next) : "memory");
}

61
chopstx-riscv32.h Normal file
View File

@@ -0,0 +1,61 @@
/*
* The thread context: specific to RISC-V 32.
*
* Calling convention matters when we implement context switch.
*
* We implement a voluntary context switch function which follows
* standard calling convension. The function doesn't need to save
* caller save registers (function arguments and temporaries) and
* fixed ones (global pointer and thread pointer) to a thread context
* struct. Only it needs to care about stack pointer and saved
* registers.
*
* For interrupt handling when it returns directly to original thread,
* we can avoid saving all registers; It can only save registers used
* in that handling. When it returns to another thread, that is, when
* it does involuntary context switch (also called "thread
* preemption"), all registers should be saved.
*
* In Chopstx, we don't use nested interrupt of machine. So, when a
* thread is interrupted, the interrupted context is always one of
* application threads or the idle thread. We have a dedicated stack
* for exception handling.
*
* Here is RISC-V calling convention.
*
* Register ABI name Description Saver other usage
* x0 zero 0, hard-wired --
* x1 ra return address caller
* x2 sp stack pointer callEE
* x3 gp global pointer --
* x4 tp thread pointer --
* x5-7 t0-2 temporary caller
* x8 s0/fp saved register callEE frame pointer
* x9 s1 saved register callEE
* x10-11 a0-1 argument caller return value
* x12-17 a2-7 argument caller
* x18-27 s2-11 saved register callEE
* x28-31 t3-6 temporary caller
*
* Simply, we have entries of all registers in the thread context. We
* don't use stack to save/restore part of its context. We don't put
* any other constraints such that an application should always keep
* gp and/or tp registers; It is free for an application to
* temporarily use those registers beyond a conventional usage (e.g.,
* for some specific routines like hash computation).
*
* We use the first entry, reg[0], to store PC, because x0 is always 0,
* no need to save/restore it.
*
* Core specific machine status information is saved into the last
* entry, MACHINE_STATUS. For Bumblebee core, it is composed by bits
* of mstatus and msubm.
*/
struct tcontext {
uint32_t reg[32];
uint32_t machine_status;
};
typedef struct tcontext tcontext_t;
#define CHOPSTX_THREAD_SIZE 160

229
chopstx.c
View File

@@ -1,7 +1,7 @@
/*
* chopstx.c - Threads and only threads.
*
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -23,7 +23,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
@@ -73,19 +73,12 @@ chx_fatal (uint32_t err_code)
}
/* Include the definition of thread context structure. */
#ifdef GNU_LINUX_EMULATION
#include "chopstx-gnu-linux.h"
#else
#include "chopstx-cortex-m.h"
#endif
#include ARCH_HEADER
/* ALLOW_SLEEP for the idle thread. */
int chx_allow_sleep;
static struct chx_spinlock chx_enable_sleep_lock;
/* RUNNING: the current thread. */
struct chx_thread *running;
struct chx_queue {
struct chx_qh q;
struct chx_spinlock lock;
@@ -104,7 +97,6 @@ static struct chx_queue q_join;
static struct chx_queue q_intr;
/* Forward declaration(s). */
static void chx_request_preemption (uint16_t prio);
static int chx_wakeup (struct chx_pq *p);
static struct chx_thread *chx_timer_insert (struct chx_thread *tp, uint32_t usec);
static uint32_t chx_timer_dequeue (struct chx_thread *tp);
@@ -271,6 +263,12 @@ chx_ready_pop (void)
tp->state = THREAD_RUNNING;
chx_spin_unlock (&q_ready.lock);
if (tp && tp->flag_sched_rr)
{
chx_spin_lock (&q_timer.lock);
chx_timer_insert (tp, PREEMPTION_USEC);
chx_spin_unlock (&q_timer.lock);
}
return tp;
}
@@ -294,15 +292,15 @@ chx_ready_enqueue (struct chx_thread *tp)
chx_spin_unlock (&q_ready.lock);
}
static struct chx_thread *chx_timer_expired (void);
static struct chx_thread *chx_recv_irq (uint32_t irq_num);
static struct chx_thread *chx_running_preempted (struct chx_thread *tp_next);
/*
* Here comes architecture specific code.
*/
#ifdef GNU_LINUX_EMULATION
#include "chopstx-gnu-linux.c"
#else
#include "chopstx-cortex-m.c"
#endif
#include ARCH_IMPL
static void
chx_set_timer (struct chx_thread *tp, uint32_t ticks)
@@ -381,14 +379,17 @@ chx_timer_dequeue (struct chx_thread *tp)
}
void
static struct chx_thread *
chx_timer_expired (void)
{
struct chx_thread *tp;
struct chx_thread *running = chx_running ();
uint16_t prio = 0; /* Use uint16_t here. */
chx_spin_lock (&q_timer.lock);
if ((tp = (struct chx_thread *)ll_pop (&q_timer.q)))
if (!(tp = (struct chx_thread *)ll_pop (&q_timer.q)))
chx_systick_reload (0);
else
{
uint32_t next_tick = tp->v;
@@ -400,7 +401,9 @@ chx_timer_expired (void)
if ((uint16_t)tp->prio > prio)
prio = (uint16_t)tp->prio;
if (!ll_empty (&q_timer.q))
if (ll_empty (&q_timer.q))
chx_systick_reload (0);
else
{
struct chx_thread *tp_next;
@@ -420,23 +423,127 @@ chx_timer_expired (void)
prio = (uint16_t)tp->prio;
}
if (!ll_empty (&q_timer.q))
if (ll_empty (&q_timer.q))
chx_systick_reload (0);
else
chx_set_timer ((struct chx_thread *)&q_timer.q, next_tick);
}
}
chx_spin_unlock (&q_timer.lock);
chx_request_preemption (prio);
if (running == NULL || (uint16_t)running->prio < prio)
{
tp = chx_ready_pop ();
if (tp != running)
return tp;
else
/* When tp->flag_sched_rr == 1, it's possible. No context switch. */
return NULL;
}
else
return NULL;
}
static struct chx_thread *
chx_recv_irq (uint32_t irq_num)
{
struct chx_pq *p;
struct chx_thread *r = chx_running ();
chx_disable_intr (irq_num);
chx_spin_lock (&q_intr.lock);
for (p = q_intr.q.next; p != (struct chx_pq *)&q_intr.q; p = p->next)
if (p->v == irq_num)
/* should be one at most. */
break;
chx_spin_unlock (&q_intr.lock);
if (p != (struct chx_pq *)&q_intr.q)
{
struct chx_px *px = (struct chx_px *)p;
ll_dequeue (p);
chx_wakeup (p);
if (r == NULL || (uint16_t)r->prio < px->master->prio)
return chx_ready_pop ();
}
return NULL;
}
static struct chx_thread * __attribute__ ((noinline))
chx_running_preempted (struct chx_thread *tp_next)
{
struct chx_thread *r = chx_running ();
if (r == NULL)
return tp_next;
if (r->flag_sched_rr)
{
if (r->state == THREAD_RUNNING)
{
chx_timer_dequeue (r);
chx_ready_enqueue (r);
}
/*
* It may be THREAD_READY after chx_timer_expired.
* Then, do nothing. It's in the ready queue.
*/
}
else
chx_ready_push (r);
return tp_next;
}
/*
* chx_sched: switch to another thread.
*
* There are two cases:
* YIELD=0 (SLEEP): Current RUNNING thread is already connected to
* something (mutex, cond, intr, etc.)
* YIELD=1 (YIELD): Current RUNNING thread is active,
* it is needed to be enqueued to READY queue.
*
* Returns:
* >= 1 on wakeup by others, value means ticks remained for sleep.
* 0 on normal wakeup (timer expiration, lock acquirement).
* -1 on cancellation.
*/
static uintptr_t __attribute__ ((noinline))
chx_sched (uint32_t yield)
{
struct chx_thread *tp;
if (yield)
{
struct chx_thread *r = chx_running ();
if (r->flag_sched_rr)
chx_timer_dequeue (r);
chx_ready_enqueue (r);
}
tp = chx_ready_pop ();
return voluntary_context_switch (tp);
}
void
chx_systick_init (void)
{
chx_systick_reset ();
chx_systick_init_arch ();
if ((CHX_FLAGS_MAIN & CHOPSTX_SCHED_RR))
{
struct chx_thread *running = chx_running ();
chx_cpu_sched_lock ();
chx_spin_lock (&q_timer.lock);
chx_timer_insert (running, PREEMPTION_USEC);
@@ -450,7 +557,7 @@ chopstx_t chopstx_main;
void
chx_init (struct chx_thread *tp)
{
chx_prio_init ();
chx_interrupt_controller_init ();
chx_init_arch (tp);
chx_spin_init (&chx_enable_sleep_lock);
@@ -475,10 +582,6 @@ chx_init (struct chx_thread *tp)
tp->prio = 0;
tp->parent = NULL;
tp->v = 0;
running = tp;
if (CHX_PRIO_MAIN_INIT >= CHOPSTX_PRIO_INHIBIT_PREEMPTION)
chx_cpu_sched_lock ();
tp->prio = CHX_PRIO_MAIN_INIT;
@@ -497,6 +600,7 @@ chx_wakeup (struct chx_pq *pq)
{
int yield = 0;
struct chx_thread *tp;
struct chx_thread *running = chx_running ();
if (pq->flag_is_proxy)
{
@@ -537,6 +641,7 @@ static void __attribute__((noreturn))
chx_exit (void *retval)
{
struct chx_pq *p;
struct chx_thread *running = chx_running ();
chx_cpu_sched_lock ();
if (running->flag_join_req)
@@ -573,6 +678,7 @@ static chopstx_prio_t
chx_mutex_unlock (chopstx_mutex_t *mutex)
{
struct chx_thread *tp;
struct chx_thread *running = chx_running ();
mutex->owner = NULL;
running->mutex_list = mutex->list;
@@ -620,6 +726,7 @@ chopstx_create (uint32_t flags_and_prio,
{
struct chx_thread *tp;
chopstx_prio_t prio = (flags_and_prio & CHOPSTX_PRIO_MASK);
struct chx_thread *running = chx_running ();
tp = chopstx_create_arch (stack_addr, stack_size, thread_entry,
arg);
@@ -665,6 +772,7 @@ chopstx_create (uint32_t flags_and_prio,
static int
chx_snooze (uint32_t state, uint32_t *usec_p)
{
struct chx_thread *running = chx_running ();
uint32_t usec = *usec_p;
uint32_t usec0;
int r;
@@ -688,7 +796,7 @@ chx_snooze (uint32_t state, uint32_t *usec_p)
*usec_p -= usec0;
else if (r > 0)
{
*usec_p -= (usec0 - r / MHZ);
*usec_p -= (usec0 - ticks_to_usec (r));
r = 1;
}
@@ -787,7 +895,7 @@ requeue (struct chx_thread *tp)
void
chopstx_mutex_lock (chopstx_mutex_t *mutex)
{
struct chx_thread *tp = running;
struct chx_thread *tp = chx_running ();
while (1)
{
@@ -846,6 +954,7 @@ chopstx_mutex_lock (chopstx_mutex_t *mutex)
void
chopstx_mutex_unlock (chopstx_mutex_t *mutex)
{
struct chx_thread *running = chx_running ();
chopstx_prio_t prio;
chx_cpu_sched_lock ();
@@ -883,7 +992,7 @@ chopstx_cond_init (chopstx_cond_t *cond)
void
chopstx_cond_wait (chopstx_cond_t *cond, chopstx_mutex_t *mutex)
{
struct chx_thread *tp = running;
struct chx_thread *tp = chx_running ();
int r;
chopstx_testcancel ();
@@ -1005,17 +1114,21 @@ chx_cond_hook (struct chx_px *px, struct chx_poll_head *pd)
void
chopstx_claim_irq (chopstx_intr_t *intr, uint8_t irq_num)
{
int intr_before_claim;
intr->type = CHOPSTX_POLL_INTR;
intr->ready = 0;
intr->irq_num = irq_num;
chx_cpu_sched_lock ();
chx_spin_lock (&q_intr.lock);
chx_disable_intr (irq_num);
intr_before_claim = chx_disable_intr (irq_num);
chx_clr_intr (irq_num);
chx_set_intr_prio (irq_num);
chx_spin_unlock (&q_intr.lock);
chx_cpu_sched_unlock ();
if (intr_before_claim)
intr->ready = 1;
}
@@ -1087,6 +1200,8 @@ chopstx_intr_done (chopstx_intr_t *intr)
void
chopstx_cleanup_push (struct chx_cleanup *clp)
{
struct chx_thread *running = chx_running ();
clp->next = running->clp;
running->clp = clp;
}
@@ -1101,6 +1216,7 @@ chopstx_cleanup_push (struct chx_cleanup *clp)
void
chopstx_cleanup_pop (int execute)
{
struct chx_thread *running = chx_running ();
struct chx_cleanup *clp = running->clp;
if (clp)
@@ -1125,6 +1241,7 @@ void
chopstx_exit (void *retval)
{
struct chx_mtx *m, *m_next;
struct chx_thread *running = chx_running ();
struct chx_cleanup *clp = running->clp;
running->clp = NULL;
@@ -1161,6 +1278,7 @@ chopstx_exit (void *retval)
int
chopstx_join (chopstx_t thd, void **ret)
{
struct chx_thread *running = chx_running ();
struct chx_thread *tp = (struct chx_thread *)thd;
int r = 0;
@@ -1264,6 +1382,7 @@ void
chopstx_cancel (chopstx_t thd)
{
struct chx_thread *tp = (struct chx_thread *)thd;
struct chx_thread *running = chx_running ();
chx_cpu_sched_lock ();
tp->flag_got_cancel = 1;
@@ -1320,6 +1439,8 @@ chopstx_cancel (chopstx_t thd)
void
chopstx_testcancel (void)
{
struct chx_thread *running = chx_running ();
if (running->flag_cancelable && running->flag_got_cancel)
chopstx_exit (CHOPSTX_CANCELED);
}
@@ -1336,6 +1457,7 @@ chopstx_testcancel (void)
int
chopstx_setcancelstate (int cancel_disable)
{
struct chx_thread *running = chx_running ();
int old_state = !running->flag_cancelable;
running->flag_cancelable = (cancel_disable == 0);
@@ -1346,6 +1468,8 @@ chopstx_setcancelstate (int cancel_disable)
static void
chx_proxy_init (struct chx_px *px, uint32_t *cp)
{
struct chx_thread *running = chx_running ();
px->next = px->prev = (struct chx_pq *)px;
px->flag_is_proxy = 1;
px->prio = running->prio;
@@ -1377,6 +1501,7 @@ chopstx_poll (uint32_t *usec_p, int n, struct chx_poll_head *const pd_array[])
struct chx_px px[n];
struct chx_poll_head *pd;
int r = 0;
struct chx_thread *running = chx_running ();
chx_dmb ();
chopstx_testcancel ();
@@ -1384,6 +1509,7 @@ chopstx_poll (uint32_t *usec_p, int n, struct chx_poll_head *const pd_array[])
for (i = 0; i < n; i++)
chx_proxy_init (&px[i], &counter);
again:
for (i = 0; i < n; i++)
{
pd = pd_array[i];
@@ -1446,6 +1572,20 @@ chopstx_poll (uint32_t *usec_p, int n, struct chx_poll_head *const pd_array[])
ll_dequeue ((struct chx_pq *)&px[i]);
chx_spin_unlock (&pc->cond->lock);
}
else
{ /* Check the condition again after woken up. */
if (pc->mutex)
chopstx_mutex_lock (pc->mutex);
if ((*pc->check) (pc->arg) == 0)
{ /* Condition doesn't met. */
pc->ready = 0;
counter--;
}
if (pc->mutex)
chopstx_mutex_unlock (pc->mutex);
}
}
else if (pd->type == CHOPSTX_POLL_INTR)
{
@@ -1477,6 +1617,9 @@ chopstx_poll (uint32_t *usec_p, int n, struct chx_poll_head *const pd_array[])
if (r < 0)
chopstx_exit (CHOPSTX_CANCELED);
if (counter == 0 && (usec_p == NULL || *usec_p))
goto again;
return counter;
}
@@ -1497,7 +1640,7 @@ chopstx_poll (uint32_t *usec_p, int n, struct chx_poll_head *const pd_array[])
chopstx_prio_t
chopstx_setpriority (chopstx_prio_t prio_new)
{
struct chx_thread *tp = running;
struct chx_thread *tp = chx_running ();
chopstx_prio_t prio_orig, prio_cur;
chx_cpu_sched_lock ();
@@ -1517,7 +1660,7 @@ chopstx_setpriority (chopstx_prio_t prio_new)
if (tp->prio < prio_cur)
chx_sched (CHX_YIELD);
else if (tp->prio < CHOPSTX_PRIO_INHIBIT_PREEMPTION)
else
chx_cpu_sched_unlock ();
return prio_orig;
@@ -1557,3 +1700,23 @@ chopstx_conf_idle (int enable_sleep)
return r;
}
/**
* chopstx_critical - Construct a critical section
* @func: function to be called
* @arg: argument to function
*
* Run the function @func with @arg under CPU schedule lock.
* Return void * pointer.
*/
void *
chopstx_critical (void *func (void *), void *arg)
{
void *p;
chx_cpu_sched_lock ();
p = func (arg);
chx_cpu_sched_unlock ();
return p;
}

View File

@@ -1,7 +1,8 @@
/*
* chopstx.h - Threads and only threads.
*
* Copyright (C) 2013, 2016, 2017, 2018 Flying Stone Technology
* Copyright (C) 2013, 2016, 2017, 2018, 2020
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -22,7 +23,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
@@ -45,8 +46,6 @@ chopstx_create (uint32_t flags_and_prio,
#define CHOPSTX_DETACHED 0x10000
#define CHOPSTX_SCHED_RR 0x20000
#define CHOPSTX_PRIO_INHIBIT_PREEMPTION 248
void chopstx_usec_wait (uint32_t usec);
struct chx_spinlock {
@@ -164,4 +163,4 @@ int chopstx_poll (uint32_t *usec_p, int n,
int chopstx_conf_idle (int enable_sleep);
#define CHOPSTX_THREAD_SIZE 64
void *chopstx_critical (void *func (void *), void *arg);

232
contrib/ackbtn-gnu-linux.c Normal file
View File

@@ -0,0 +1,232 @@
/*
* ackbtn-gnu-linux.c - Acknowledge button support for GNU/Linux
*
* Copyright (C) 2021 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <pthread.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <chopstx.h>
#include <stdio.h>
#include <signal.h>
#include <sys/eventfd.h>
#include <poll.h>
#include <errno.h>
#include <stdlib.h>
static pthread_t tid_main;
static pthread_t tid_ui;
static pthread_mutex_t mutex;
static pthread_cond_t cond;
static int enabled;
static int event_fd;
static void
ackbtn_intr (int signum, siginfo_t *siginfo, void *arg)
{
extern void chx_sigmask (ucontext_t *uc);
extern void chx_handle_intr (int signum);
ucontext_t *uc = arg;
(void)signum;
(void)siginfo;
chx_handle_intr (SIGUSR2);
chx_sigmask (uc);
}
#define ACKBTN_ACKED (1 << 0)
#define ACKBTN_TIMEOUT (1 << 2)
static void *
user_interaction (void *arg)
{
struct pollfd pollfds[2];
(void)arg;
pollfds[0].fd = 0; /* standard input */
pollfds[0].events = POLLIN;
pollfds[1].fd = event_fd;
pollfds[1].events = POLLIN;
fputs ("User interaction thread for AckBtn started.\n", stdout);
while (1)
{
unsigned int acked_or_timeout = 0;
char buf[256];
pthread_mutex_lock (&mutex);
while (!enabled)
pthread_cond_wait (&cond, &mutex);
pthread_mutex_unlock (&mutex);
/* Consume all input if any. */
while (1)
{
int r;
pollfds[0].revents = 0;
r = poll (pollfds, 1, 0);
if (r < 0)
{
if (errno == EINTR)
continue;
perror ("poll");
exit (1);
}
if (r == 0)
break;
if ((pollfds[0].revents & POLLIN))
read (0, buf, sizeof buf);
}
fputs ("Type RET to acknowledge (or wait for timeout) > ", stdout);
fflush (stdout);
while (!acked_or_timeout)
{
pollfds[0].revents = 0;
pollfds[1].revents = 0;
if (poll (pollfds, 2, -1) < 0)
{
if (errno == EINTR)
continue;
perror ("poll");
exit (1);
}
if ((pollfds[0].revents & POLLIN))
{
read (0, buf, sizeof buf);
acked_or_timeout |= ACKBTN_ACKED;
}
if ((pollfds[1].revents & POLLIN))
acked_or_timeout |= ACKBTN_TIMEOUT;
}
pthread_mutex_lock (&mutex);
if ((acked_or_timeout & ACKBTN_ACKED))
{
if ((acked_or_timeout & ACKBTN_TIMEOUT))
/* No output of newline, as it follows timeout message. */
fputs ("Input ignored", stdout);
else
{
pthread_kill (tid_main, SIGUSR2);
fputs ("Acknowledged\n", stdout);
}
}
if ((acked_or_timeout & ACKBTN_TIMEOUT))
fputs ("\nTimeout\n", stdout);
while (enabled)
pthread_cond_wait (&cond, &mutex);
read (event_fd, buf, sizeof (uint64_t));
pthread_mutex_unlock (&mutex);
}
}
void
ackbtn_init (chopstx_intr_t *intr)
{
int r;
sigset_t sigset;
struct sigaction act;
event_fd = eventfd (0, EFD_CLOEXEC);
if (event_fd < 0)
{
perror ("eventfd");
exit (1);
}
pthread_mutex_init (&mutex, NULL);
pthread_cond_init (&cond, NULL);
enabled = 0;
sigemptyset (&sigset);
sigaddset (&sigset, SIGUSR2);
sigaddset (&sigset, SIGALRM);
tid_main = pthread_self ();
/* Launch the thread for user interaction. */
pthread_sigmask (SIG_BLOCK, &sigset, NULL);
r = pthread_create (&tid_ui, NULL, user_interaction, NULL);
if (r)
{
fprintf (stderr, "ackbtn_init: %s\n", strerror (r));
exit (1);
}
act.sa_sigaction = ackbtn_intr;
sigfillset (&act.sa_mask);
act.sa_flags = SA_SIGINFO|SA_RESTART;
sigaction (SIGUSR2, &act, NULL);
pthread_sigmask (SIG_UNBLOCK, &sigset, NULL);
chopstx_claim_irq (intr, SIGUSR2);
}
void
ackbtn_enable (void)
{
pthread_mutex_lock (&mutex);
enabled = 1;
pthread_cond_signal (&cond);
pthread_mutex_unlock (&mutex);
}
void
ackbtn_disable (void)
{
const uint64_t l = 1;
pthread_mutex_lock (&mutex);
enabled = 0;
write (event_fd, &l, sizeof (l));
pthread_cond_signal (&cond);
pthread_mutex_unlock (&mutex);
}

View File

@@ -22,7 +22,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
@@ -57,6 +57,7 @@ ackbtn_init (chopstx_intr_t *intr)
{
case BOARD_ID_FST_01:
case BOARD_ID_FST_01G:
case BOARD_ID_GNUKEY_DS:
/* PA2 can be connected to a hall sensor or a switch */
afio_exticr_index = 0;
afio_exticr_extiX_pY = AFIO_EXTICR1_EXTI2_PA;

View File

@@ -3,7 +3,7 @@
* This ADC driver just fills pseudo random values.
* It's completely useless other than for NeuG.
*
* Copyright (C) 2017 Free Software Initiative of Japan
* Copyright (C) 2017, 2021 Free Software Initiative of Japan
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
@@ -24,7 +24,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
@@ -33,7 +33,7 @@
#include <chopstx.h>
#include "adc.h"
#define ADC_RANDOM_SEED 0x01034649 /* "Hello, father!" in Japanese */
#include <sys/random.h>
/*
* Do calibration for ADC.
@@ -41,7 +41,6 @@
int
adc_init (void)
{
srandom (ADC_RANDOM_SEED);
return 0;
}
@@ -55,8 +54,7 @@ uint32_t adc_buf[64];
void
adc_start_conversion (int offset, int count)
{
while (count--)
adc_buf[offset++] = random ();
getrandom (adc_buf+offset, count, 0);
}

View File

@@ -25,7 +25,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/

View File

@@ -25,7 +25,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/

207
contrib/spi-st.c Normal file
View File

@@ -0,0 +1,207 @@
/*
* spi-st.c - SPI driver for STM32F103/GD32F103/GD32VF103
*
* Copyright (C) 2019 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <mcu/stm32.h>
#ifndef SPI_USE_INTERRUPT
#define SPI_BUSY_WAIT 1
#endif
/* Hardware registers */
struct SPI {
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t CRCPR;
volatile uint32_t RXCRCR;
volatile uint32_t TXCRCR;
};
/* It is named SPI0 in GD32VF103, while it's "SPI1" in RM0008 of STM32F103 */
#define SPI0_BASE (APB2PERIPH_BASE + 0x3000)
#define SPI0 ((struct SPI *)SPI0_BASE)
#define INTR_REQ_SPI0 54 /* For GD32VF103 */
static struct SPI *SPIx = SPI0;
#ifndef SPI_BUSY_WAIT
static struct chx_intr spi_intr;
#endif
static int tx_ready;
static int rx_done;
/*
* For now, it only support configuration of a specific use case:
*
* SPI0 of GD32VF103
* Master mode
* Hardware NSS pin not used
* Two unidirectional lines, full duplex
* CRC enabled
* 8-bit data frame
* MSBit first
* Clock polarity: High==idle
*
* We need to consider what kind of configuration can be specified
* by the API. Perhaps, it should return an error code when it is
* not supported by the hardware.
*/
int
spi_init (void)
{
uint32_t cr1_reg = 0;
/* Enable SPI module */
RCC->APB2ENR |= (1 << 12);
RCC->APB2RSTR = (1 << 12);
RCC->APB2RSTR = 0;
/* Master mode */
cr1_reg |= (1 << 2); /* In RM0008, it's called MSTR */
/* No use of hardware NSS pin */
cr1_reg |= (1 << 9); /* In RM0008, it's called SSM */
cr1_reg |= (1 << 8); /* In RM0008, it's called SSI */
/* Two unidirectional lines, full duplex: Bit15=0, Bit14=0, Bit10=0 */
/* Bit11: data-frame-format: 8-bit 0 (16-bit (1<<11)) */
/* But7: bit-order: MSB first 0, LSB first (1<<7) */
/* Enable CRC */
cr1_reg |= (1 << 13);
cr1_reg |= (0x02 << 3); /* prescaler: fPCLK/8 */
cr1_reg |= (1 << 1); /* clock polarity 1==idle */
cr1_reg |= (1 << 0); /* second clock data capture edge */
cr1_reg |= (1 << 6); /* SPI enable */
SPIx->CR1 = cr1_reg;
/* Don't touch CRCPR: Just use reset default value of 7 */
/* Don't touch I2SCFGR, I2SPR */
#ifndef SPI_BUSY_WAIT
chopstx_claim_irq (&spi_intr, INTR_REQ_SPI0);
/* Enable notification for Tx empty, Rx done, or Error */
SPIx->CR2 = (1 << 7) | (1 << 6) | (1 << 5);
#endif
tx_ready = 0;
rx_done = 0;
return 0;
}
static void
clear_crc_err (void)
{
SPIx->SR &= ~(1 << 4);
}
static void
put_data (uint16_t data)
{
tx_ready = 0;
rx_done = 0;
SPIx->DR = (uint32_t)data;
}
static uint16_t
get_data (void)
{
return ((uint16_t)SPIx->DR);
}
#define CHECK_TX 0
#define CHECK_RX 1
static void
check_transmit (int for_what)
{
for (;;)
{
uint16_t status;
if (for_what == CHECK_TX && tx_ready)
return;
if (for_what == CHECK_RX && rx_done)
return;
#ifndef SPI_BUSY_WAIT
/* Wait an event */
chopstx_intr_wait (&spi_intr);
#endif
status = SPIx->SR;
if ((status & (1 << 0)))
rx_done = 1;
if ((status & (1 << 1)))
tx_ready = 1;
if ((status & (1 << 4)))
{
/*FIXME: stat crc_err_count++ */
clear_crc_err ();
}
#if 0
/*FIXME: stat err_count++ */
if ((status & 0x00fc))
{
}
#endif
#ifndef SPI_BUSY_WAIT
chopstx_intr_done (&spi_intr);
#endif
}
}
void
spi_send (uint16_t data)
{
check_transmit (CHECK_TX);
put_data (data);
}
uint16_t
spi_recv (void)
{
check_transmit (CHECK_RX);
return get_data ();
}

3
contrib/spi.h Normal file
View File

@@ -0,0 +1,3 @@
int spi_init (void);
void spi_send (uint16_t data);
uint16_t spi_recv (void);

386
contrib/usart-common-f103.c Normal file
View File

@@ -0,0 +1,386 @@
static void
usart_config_recv_enable (struct USART *USARTx, int on)
{
if (on)
USARTx->CR1 |= USART_CR1_RE;
else
USARTx->CR1 &= ~USART_CR1_RE;
}
int
usart_config (uint8_t dev_no, uint32_t config_bits)
{
struct USART *USARTx = get_usart_dev (dev_no);
uint8_t baud_spec = (config_bits & MASK_BAUD);
int i;
uint32_t cr1_config = (USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE);
/* TXEIE/TCIE will be enabled when
putting char */
/* No CTSIE, PEIE, IDLEIE, LBDIE */
if (USARTx == NULL)
return -1;
/* Disable USART before configure. */
USARTx->CR1 &= ~USART_CR1_UE;
if (((config_bits & MASK_CS) == CS7 && (config_bits & PARENB))
|| ((config_bits & MASK_CS) == CS8 && (config_bits & PARENB) == 0))
cr1_config &= ~USART_CR1_M;
else if ((config_bits & MASK_CS) == CS8)
cr1_config |= USART_CR1_M;
else
return -1;
if ((config_bits & PARENB) == 0)
cr1_config &= ~(USART_CR1_PCE | USART_CR1_PEIE);
else
cr1_config |= (USART_CR1_PCE | USART_CR1_PEIE);
if ((config_bits & PARODD) == 0)
cr1_config &= ~USART_CR1_PS;
else
cr1_config |= USART_CR1_PS;
if ((config_bits & MASK_STOP) == STOP0B5)
USARTx->CR2 = (0x1 << 12);
else if ((config_bits & MASK_STOP) == STOP1B)
USARTx->CR2 = (0x0 << 12);
else if ((config_bits & MASK_STOP) == STOP1B5)
USARTx->CR2 = (0x3 << 12);
else /* if ((config_bits & MASK_STOP) == STOP2B) */
USARTx->CR2 = (0x2 << 12);
for (i = 0; i < NUM_BAUD; i++)
if (brr_table[i].baud_spec == baud_spec)
break;
if (i >= NUM_BAUD)
return -1;
USARTx->BRR = brr_table[i].brr_value;
if ((config_bits & MASK_FLOW))
USARTx->CR3 = USART_CR3_CTSE | USART_CR3_RTSE;
else
USARTx->CR3 = 0;
if (!(config_bits & MASK_MODE))
cr1_config |= USART_CR1_RE;
USARTx->CR1 = cr1_config;
/* SCEN (smartcard enable) should be set _after_ CR1. */
if ((config_bits & MASK_MODE))
{
if ((config_bits & MASK_MODE) == MODE_SMARTCARD)
{
USARTx->GTPR = (1 << 8) | 5;
USARTx->CR3 |= (USART_CR3_SCEN | USART_CR3_NACK);
}
else if ((config_bits & MASK_MODE) == MODE_IRDA)
USARTx->CR3 |= (1 << 1);
else if ((config_bits & MASK_MODE) == MODE_IRDA_LP)
USARTx->CR3 |= (1 << 2) | (1 << 1);
}
return 0;
}
void
usart_init0 (int (*cb) (uint8_t dev_no, uint16_t notify_bits))
{
int i;
ss_notify_callback = cb;
for (i = 0; i < NUM_USART; i++)
{
if (usart_array[i].stat)
usart_array[i].stat->dev_no = i + USART_DEVNO_START;
chopstx_claim_irq (usart_array[i].intr, usart_array[i].irq_num);
}
usart_rcc_setup ();
}
#define UART_STATE_BITMAP_RX_CARRIER (1 << 0)
#define UART_STATE_BITMAP_TX_CARRIER (1 << 1)
#define UART_STATE_BITMAP_BREAK (1 << 2)
#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3)
#define UART_STATE_BITMAP_FRAMING (1 << 4)
#define UART_STATE_BITMAP_PARITY (1 << 5)
#define UART_STATE_BITMAP_OVERRUN (1 << 6)
static int
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
{
int tx_ready = 0;
uint32_t r = USARTx->SR;
int notify_bits = 0;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
if (smartcard_mode)
{
if ((r & USART_SR_TC))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TCIE;
}
}
else
{
if ((r & USART_SR_TXE))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TXEIE;
}
}
if ((r & USART_SR_RXNE))
{
uint32_t data = USARTx->DR;
/* DR register should be accessed even if data is not used.
* Its read-access has side effect of clearing error flags.
*/
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_NE))
stat->err_rx_noise++;
else if ((r & USART_SR_FE))
{
/* NOTE: Noway to distinguish framing error and break */
stat->rx_break++;
notify_bits |= UART_STATE_BITMAP_BREAK;
}
else if ((r & USART_SR_PE))
{
stat->err_rx_parity++;
notify_bits |= UART_STATE_BITMAP_PARITY;
}
else
{
if ((r & USART_SR_ORE))
{
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
stat->err_rx_overflow++;
else
stat->rx++;
}
}
else if ((r & USART_SR_ORE))
{ /* Clear ORE */
uint32_t data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
if (notify_bits)
{
if (ss_notify_callback
&& (*ss_notify_callback) (stat->dev_no, notify_bits))
stat->err_notify_overflow++;
}
return tx_ready;
}
static int
handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat)
{
int tx_ready = 1;
int c = rb_ll_get (rb2h);
if (c >= 0)
{
uint32_t r;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
USARTx->DR = (c & 0xff);
stat->tx++;
r = USARTx->SR;
if (smartcard_mode)
{
if ((r & USART_SR_TC) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TCIE;
}
}
else
{
if ((r & USART_SR_TXE) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TXEIE;
}
}
}
return tx_ready;
}
int
usart_send_break (uint8_t dev_no)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (USARTx == NULL)
return -1;
if ((USARTx->CR1 & 0x01))
return 1; /* Busy sending break, which was requested before. */
USARTx->CR1 |= 0x01;
return 0;
}
int
usart_block_sendrecv (uint8_t dev_no, const char *s_buf, uint16_t s_buflen,
char *r_buf, uint16_t r_buflen,
uint32_t *timeout_block_p, uint32_t timeout_char)
{
uint32_t timeout;
uint8_t *p;
int len;
uint32_t r;
uint32_t data;
struct USART *USARTx = get_usart_dev (dev_no);
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
struct chx_intr *usartx_intr = get_usart_intr (dev_no);
struct chx_poll_head *ph[1];
if (usartx_intr == NULL)
return -1;
ph[0] = (struct chx_poll_head *)usartx_intr;
p = (uint8_t *)s_buf;
if (p)
{
if (smartcard_mode)
usart_config_recv_enable (USARTx, 0);
USARTx->CR1 |= USART_CR1_TXEIE;
/* Sending part */
while (1)
{
chopstx_poll (NULL, 1, ph);
r = USARTx->SR;
/* Here, ignore recv error(s). */
if ((r & USART_SR_RXNE) || (r & USART_SR_ORE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
if ((r & USART_SR_TXE))
{
if (s_buflen == 0)
break;
else
{
/* Keep TXEIE bit */
USARTx->DR = *p++;
s_buflen--;
}
}
chopstx_intr_done (usartx_intr);
}
USARTx->CR1 &= ~USART_CR1_TXEIE;
if (smartcard_mode)
{
if (timeout_block_p && (*timeout_block_p))
do
r = USARTx->SR;
while (((r & USART_SR_TC) == 0));
usart_config_recv_enable (USARTx, 1);
if (timeout_block_p && *timeout_block_p == 0)
{
/* Ignoring the echo back. */
do
r = USARTx->SR;
while (((r & USART_SR_TC) == 0));
if ((r & USART_SR_RXNE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
*timeout_block_p = timeout_char;
}
}
chopstx_intr_done (usartx_intr);
}
if (r_buf == NULL)
return 0;
if (!p)
if (smartcard_mode)
usart_config_recv_enable (USARTx, 1);
/* Receiving part */
r = chopstx_poll (timeout_block_p, 1, ph);
if (r == 0)
return 0;
p = (uint8_t *)r_buf;
len = 0;
while (1)
{
r = USARTx->SR;
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_RXNE))
{
if ((r & USART_SR_NE) || (r & USART_SR_FE) || (r & USART_SR_PE))
/* ignore error, for now. XXX: ss_notify */
;
else
{
*p++ = (data & 0xff);
len++;
r_buflen--;
if (r_buflen == 0)
{
chopstx_intr_done (usartx_intr);
break;
}
}
}
else if ((r & USART_SR_ORE))
{
data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
}
chopstx_intr_done (usartx_intr);
timeout = timeout_char;
r = chopstx_poll (&timeout, 1, ph);
if (r == 0)
break;
}
return len;
}

448
contrib/usart-common.c Normal file
View File

@@ -0,0 +1,448 @@
static void *usart_main (void *arg);
/*
* Ring buffer
*/
#define MAX_RB_BUF 1024
struct rb {
uint8_t *buf;
chopstx_mutex_t m;
chopstx_cond_t data_available;
chopstx_cond_t space_available;
uint32_t head :10;
uint32_t tail :10;
uint32_t size :10;
uint32_t full : 1;
uint32_t empty : 1;
};
/* full && empty -> data is consumed fully */
/*
* Note: size = 1024 can still work, regardless of the limit of 10-bit.
*/
static void
rb_init (struct rb *rb, uint8_t *p, uint16_t size)
{
rb->buf = p;
rb->size = size;
chopstx_mutex_init (&rb->m);
chopstx_cond_init (&rb->data_available);
chopstx_cond_init (&rb->space_available);
rb->head = rb->tail = 0;
rb->full = 0;
rb->empty = 1;
}
static void
rb_add (struct rb *rb, uint8_t v)
{
rb->buf[rb->tail++] = v;
if (rb->tail == rb->size)
rb->tail = 0;
if (rb->tail == rb->head)
rb->full = 1;
else
rb->full = 0;
rb->empty = 0;
}
static uint8_t
rb_del (struct rb *rb)
{
uint32_t v = rb->buf[rb->head++];
if (rb->head == rb->size)
rb->head = 0;
if (rb->head == rb->tail)
rb->empty = 1;
rb->full = 0;
return v;
}
/*
* Application: consumer
* Hardware: generator
*/
static int
rb_ll_put (struct rb *rb, uint8_t v)
{
int r;
chopstx_mutex_lock (&rb->m);
if (rb->full && !rb->empty)
r = -1;
else
{
r = 0;
rb_add (rb, v);
chopstx_cond_signal (&rb->data_available);
}
chopstx_mutex_unlock (&rb->m);
return r;
}
/*
* Application: generator
* Hardware: consumer
*/
static int
rb_ll_get (struct rb *rb)
{
int r;
chopstx_mutex_lock (&rb->m);
if (rb->empty)
{
if (!rb->full)
rb->full = 1;
r = -1;
}
else
r = rb_del (rb);
chopstx_cond_signal (&rb->space_available);
chopstx_mutex_unlock (&rb->m);
return r;
}
static void
rb_ll_flush (struct rb *rb)
{
chopstx_mutex_lock (&rb->m);
while (!rb->empty)
rb_del (rb);
chopstx_cond_signal (&rb->space_available);
chopstx_mutex_unlock (&rb->m);
}
/*
* Application: consumer
* Hardware: generator
*/
static int
rb_read (struct rb *rb, uint8_t *buf, uint16_t buflen)
{
int i = 0;
chopstx_mutex_lock (&rb->m);
while (rb->empty)
chopstx_cond_wait (&rb->data_available, &rb->m);
while (i < buflen)
{
buf[i++] = rb_del (rb);
if (rb->empty)
break;
}
chopstx_cond_signal (&rb->space_available);
chopstx_mutex_unlock (&rb->m);
return i;
}
/*
* Application: generator
* Hardware: consumer
*/
static void
rb_write (struct rb *rb, uint8_t *buf, uint16_t buflen)
{
int i = 0;
chopstx_mutex_lock (&rb->m);
do
{
while (rb->full && !rb->empty)
chopstx_cond_wait (&rb->space_available, &rb->m);
while (i < buflen)
{
rb_add (rb, buf[i++]);
if (rb->full)
{
chopstx_cond_signal (&rb->data_available);
break;
}
}
}
while (i < buflen);
if (i)
chopstx_cond_signal (&rb->data_available);
chopstx_mutex_unlock (&rb->m);
}
static int
rb_empty_check (void *arg)
{
struct rb *rb = arg;
return rb->empty == 0;
}
/* Can be used two ways:
*
* When the ring buffer is rb_a2h:
* Hardware-side polling if data is available from application.
*
* When the ring buffer is rb_h2a:
* Application-side polling if data is available from hardware.
*/
static void
rb_get_prepare_poll (struct rb *rb, chopstx_poll_cond_t *poll_desc)
{
poll_desc->type = CHOPSTX_POLL_COND;
poll_desc->ready = 0;
poll_desc->cond = &rb->data_available;
poll_desc->mutex = &rb->m;
poll_desc->check = rb_empty_check;
poll_desc->arg = rb;
}
const struct usart_stat *
usart_stat (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].stat;
}
static struct USART *
get_usart_dev (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].USART;
}
static struct rb *
get_usart_rb_h2a (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].rb_h2a;
}
static struct rb *
get_usart_rb_a2h (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].rb_a2h;
}
static struct chx_intr *
get_usart_intr (uint8_t dev_no)
{
#if USART_DEVNO_START
if (dev_no < USART_DEVNO_START)
return NULL;
#endif
if (dev_no > USART_DEVNO_END)
return NULL;
return usart_array[dev_no - USART_DEVNO_START].intr;
}
void
usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size,
int (*cb) (uint8_t dev_no, uint16_t notify_bits))
{
usart_init0 (cb);
chopstx_create (prio, stack_addr, stack_size, usart_main, NULL);
}
static int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits);
static struct chx_poll_head *usart_poll[NUM_USART*2];
static void *
usart_main (void *arg)
{
int i;
(void)arg;
for (i = 0; i < NUM_USART; i++)
if (usart_array[i].tx_ready)
{
*usart_array[i].tx_ready = 1;
rb_init (usart_array[i].rb_a2h, usart_array[i].buf_a2h, BUF_A2H_SIZE);
rb_init (usart_array[i].rb_h2a, usart_array[i].buf_h2a, BUF_H2A_SIZE);
rb_get_prepare_poll (usart_array[i].rb_a2h, usart_array[i].app_write_event);
}
while (1)
{
int n = 0;
for (i = 0; i < NUM_USART; i++)
if (usart_array[i].tx_ready)
{
usart_poll[n++] = (struct chx_poll_head *)usart_array[i].intr;
if (*usart_array[i].tx_ready)
usart_poll[n++] = (struct chx_poll_head *)usart_array[i].app_write_event;
else
usart_array[i].app_write_event->ready = 0;
}
chopstx_poll (NULL, n, usart_poll);
for (i = 0; i < NUM_USART; i++)
if (usart_array[i].tx_ready)
{
int tx_done = 0;
if (usart_array[i].intr->ready)
{
tx_done = handle_intr (usart_array[i].USART,
usart_array[i].rb_h2a, usart_array[i].stat);
*usart_array[i].tx_ready |= tx_done;
chopstx_intr_done (usart_array[i].intr);
}
if (tx_done || (*usart_array[i].tx_ready
&& usart_array[i].app_write_event->ready))
*usart_array[i].tx_ready = handle_tx (usart_array[i].USART,
usart_array[i].rb_a2h, usart_array[i].stat);
}
}
return NULL;
}
int
usart_read (uint8_t dev_no, char *buf, uint16_t buflen)
{
struct rb *rb = get_usart_rb_h2a (dev_no);
if (rb == NULL)
return -1;
if (buf == NULL && buflen == 0)
{
rb_ll_flush (rb);
return 0;
}
else
return rb_read (rb, (uint8_t *)buf, buflen);
}
void
usart_read_prepare_poll (uint8_t dev_no, chopstx_poll_cond_t *poll_desc)
{
struct rb *rb = get_usart_rb_h2a (dev_no);
if (rb == NULL)
return;
rb_get_prepare_poll (rb, poll_desc);
}
int
usart_read_ext (uint8_t dev_no, char *buf, uint16_t buflen, uint32_t *timeout_p)
{
chopstx_poll_cond_t poll_desc;
struct chx_poll_head *ph[] = { (struct chx_poll_head *)&poll_desc };
int r;
struct rb *rb = get_usart_rb_h2a (dev_no);
if (rb == NULL)
return -1;
rb_get_prepare_poll (rb, &poll_desc);
r = chopstx_poll (timeout_p, 1, ph);
if (r == 0)
return 0;
else
return rb_read (rb, (uint8_t *)buf, buflen);
}
static void
usart_wait_write_completion (struct rb *rb)
{
chopstx_mutex_lock (&rb->m);
while (!(rb->empty && rb->full))
chopstx_cond_wait (&rb->space_available, &rb->m);
chopstx_mutex_unlock (&rb->m);
}
int
usart_write (uint8_t dev_no, char *buf, uint16_t buflen)
{
struct rb *rb = get_usart_rb_a2h (dev_no);
if (rb == NULL)
return -1;
if (buf == NULL && buflen == 0)
rb_ll_flush (rb);
else
{
struct USART *USARTx = get_usart_dev (dev_no);
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
if (smartcard_mode)
usart_config_recv_enable (USARTx, 0);
rb_write (rb, (uint8_t *)buf, buflen);
if (smartcard_mode)
{
usart_wait_write_completion (rb);
usart_config_recv_enable (USARTx, 1);
}
}
return 0;
}
int
usart_config_baud (uint8_t dev_no, uint8_t baud_spec)
{
struct USART *USARTx = get_usart_dev (dev_no);
uint32_t save_bits;
int i;
for (i = 0; i < NUM_BAUD; i++)
if (brr_table[i].baud_spec == baud_spec)
break;
if (i >= NUM_BAUD)
return -1;
save_bits = USARTx->CR1 & (USART_CR1_TE | USART_CR1_RE);
USARTx->CR1 &= ~(USART_CR1_TE | USART_CR1_RE | USART_CR1_UE);
USARTx->BRR = brr_table[i].brr_value;
USARTx->CR1 |= (save_bits | USART_CR1_UE);
return 0;
}
void
usart_config_clken (uint8_t dev_no, int on)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (on)
USARTx->CR2 |= (1 << 11);
else
USARTx->CR2 &= ~(1 << 11);
}

101
contrib/usart-gd32vf103.c Normal file
View File

@@ -0,0 +1,101 @@
/*
* usart-gd32vf103.c - USART driver for GD32VF103 (USART0)
*
* Copyright (C) 2017, 2019 g10 Code GmbH
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <mcu/stm32.h>
#include <contrib/usart.h>
#include <contrib/usart-impl.h>
#include <contrib/usart-impl-f103.h>
#define USART0_BASE (APB2PERIPH_BASE + 0x3800)
#define USART0 ((struct USART *)USART0_BASE)
static struct usart_stat usart0_stat;
static struct chx_intr usart0_intr;
#define BUF_A2H_SIZE 256
#define BUF_H2A_SIZE 512
static uint8_t buf_usart0_rb_a2h[BUF_A2H_SIZE];
static uint8_t buf_usart0_rb_h2a[BUF_H2A_SIZE];
static struct rb usart0_rb_a2h;
static struct rb usart0_rb_h2a;
static chopstx_poll_cond_t usart0_app_write_event;
/* Global variables so that it can be easier to debug. */
static int usart0_tx_ready;
#define INTR_REQ_USART0 56
#define USART_DEVNO_START 0
#define USART_DEVNO_END 0
static const struct usart usart_array[] =
{
{ USART0, &usart0_intr, INTR_REQ_USART0,
&usart0_stat, &usart0_rb_a2h, &usart0_rb_h2a, buf_usart0_rb_a2h,
buf_usart0_rb_h2a, &usart0_app_write_event, &usart0_tx_ready },
};
/* It's 96MHz f_PCLK */
static const struct brr_setting brr_table[] = {
/* Can't be represented in 16-bit { B600, (10000 << 4)}, */
/* Can't be represented in 16-bit { B1200, (5000 << 4)}, */
{ B2400, ( 2500 << 4)|0},
{ B9600, ( 625 << 4)|0},
{ B19200, ( 312 << 4)|8},
{ B57600, ( 104 << 4)|3},
{ B115200, ( 52 << 4)|1},
{ B230400, ( 26 << 4)|0},
{ B460800, ( 13 << 4)|0},
{ B921600, ( 6 << 4)|8},
{ BSCARD1, ( 620 << 4)|0}, /* 9677 */
{ BSCARD2, ( 310 << 4)|0}, /* 19354 */
{ BSCARD4, ( 155 << 4)|0}, /* 38709 */
{ BSCARD8, ( 77 << 4)|8}, /* 77419 */
{ BSCARD12, ( 51 << 4)|11}, /* 116129 */
{ BSCARD16, ( 38 << 4)|13}, /* 154506 */
{ BSCARD20, ( 31 << 4)|0}, /* 193548 */
};
#include "usart-common.c"
static void
usart_rcc_setup (void)
{
/* Enable USART0 clock, and strobe reset. */
RCC->APB2ENR |= (1 << 14);
RCC->APB2RSTR = (1 << 14);
RCC->APB2RSTR = 0;
}
#include "usart-common-f103.c"

42
contrib/usart-impl-f103.h Normal file
View File

@@ -0,0 +1,42 @@
/* Hardware registers */
struct USART {
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t BRR;
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t CR3;
volatile uint32_t GTPR;
};
#define USART_SR_CTS (1 << 9)
#define USART_SR_LBD (1 << 8)
#define USART_SR_TXE (1 << 7)
#define USART_SR_TC (1 << 6)
#define USART_SR_RXNE (1 << 5)
#define USART_SR_IDLE (1 << 4)
#define USART_SR_ORE (1 << 3)
#define USART_SR_NE (1 << 2)
#define USART_SR_FE (1 << 1)
#define USART_SR_PE (1 << 0)
#define USART_CR1_UE (1 << 13)
#define USART_CR1_M (1 << 12)
#define USART_CR1_WAKE (1 << 11)
#define USART_CR1_PCE (1 << 10)
#define USART_CR1_PS (1 << 9)
#define USART_CR1_PEIE (1 << 8)
#define USART_CR1_TXEIE (1 << 7)
#define USART_CR1_TCIE (1 << 6)
#define USART_CR1_RXNEIE (1 << 5)
#define USART_CR1_IDLEIE (1 << 4)
#define USART_CR1_TE (1 << 3)
#define USART_CR1_RE (1 << 2)
#define USART_CR1_RWU (1 << 1)
#define USART_CR1_SBK (1 << 0)
#define USART_CR3_CTSE (1 << 9)
#define USART_CR3_RTSE (1 << 8)
#define USART_CR3_SCEN (1 << 5)
#define USART_CR3_NACK (1 << 4)

25
contrib/usart-impl.h Normal file
View File

@@ -0,0 +1,25 @@
struct usart {
struct USART *USART;
struct chx_intr *intr;
uint8_t irq_num;
struct usart_stat *stat;
struct rb *rb_a2h;
struct rb *rb_h2a;
uint8_t *buf_a2h;
uint8_t *buf_h2a;
chopstx_poll_cond_t *app_write_event;
int *tx_ready;
};
static int handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat);
static int handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat);
static void usart_config_recv_enable (struct USART *USARTx, int on);
struct brr_setting {
uint8_t baud_spec;
uint32_t brr_value;
};
#define NUM_BAUD (int)(sizeof (brr_table) / sizeof (struct brr_setting))
#define NUM_USART ((int)(sizeof (usart_array) / sizeof (struct usart)))

View File

@@ -22,7 +22,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
@@ -31,68 +31,56 @@
#include <chopstx.h>
#include <mcu/stm32.h>
#include <contrib/usart.h>
struct USART {
volatile uint32_t SR;
volatile uint32_t DR;
volatile uint32_t BRR;
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t CR3;
volatile uint32_t GTPR;
};
#include <contrib/usart-impl.h>
#include <contrib/usart-impl-f103.h>
#define USART2_BASE (APB1PERIPH_BASE + 0x4400)
#define USART3_BASE (APB1PERIPH_BASE + 0x4800)
static struct USART *const USART2 = (struct USART *)USART2_BASE;
static struct USART *const USART3 = (struct USART *)USART3_BASE;
#define USART2 ((struct USART *)USART2_BASE)
#define USART3 ((struct USART *)USART3_BASE)
#define USART_SR_CTS (1 << 9)
#define USART_SR_LBD (1 << 8)
#define USART_SR_TXE (1 << 7)
#define USART_SR_TC (1 << 6)
#define USART_SR_RXNE (1 << 5)
#define USART_SR_IDLE (1 << 4)
#define USART_SR_ORE (1 << 3)
#define USART_SR_NE (1 << 2)
#define USART_SR_FE (1 << 1)
#define USART_SR_PE (1 << 0)
static struct usart_stat usart2_stat;
static struct usart_stat usart3_stat;
static struct chx_intr usart2_intr;
static struct chx_intr usart3_intr;
#define USART_CR1_UE (1 << 13)
#define USART_CR1_M (1 << 12)
#define USART_CR1_WAKE (1 << 11)
#define USART_CR1_PCE (1 << 10)
#define USART_CR1_PS (1 << 9)
#define USART_CR1_PEIE (1 << 8)
#define USART_CR1_TXEIE (1 << 7)
#define USART_CR1_TCIE (1 << 6)
#define USART_CR1_RXNEIE (1 << 5)
#define USART_CR1_IDLEIE (1 << 4)
#define USART_CR1_TE (1 << 3)
#define USART_CR1_RE (1 << 2)
#define USART_CR1_RWU (1 << 1)
#define USART_CR1_SBK (1 << 0)
#define BUF_A2H_SIZE 256
#define BUF_H2A_SIZE 512
static uint8_t buf_usart2_rb_a2h[BUF_A2H_SIZE];
static uint8_t buf_usart2_rb_h2a[BUF_H2A_SIZE];
static uint8_t buf_usart3_rb_a2h[BUF_A2H_SIZE];
static uint8_t buf_usart3_rb_h2a[BUF_H2A_SIZE];
static struct rb usart2_rb_a2h;
static struct rb usart2_rb_h2a;
static struct rb usart3_rb_a2h;
static struct rb usart3_rb_h2a;
static struct USART *
get_usart_dev (uint8_t dev_no)
static chopstx_poll_cond_t usart2_app_write_event;
static chopstx_poll_cond_t usart3_app_write_event;
/* Global variables so that it can be easier to debug. */
static int usart2_tx_ready;
static int usart3_tx_ready;
#define INTR_REQ_USART2 38
#define INTR_REQ_USART3 39
#define USART_DEVNO_START 2
#define USART_DEVNO_END 3
static const struct usart usart_array[] =
{
if (dev_no == 2)
return USART2;
else if (dev_no == 3)
return USART3;
return NULL;
}
{ USART2, &usart2_intr, INTR_REQ_USART2,
&usart2_stat, &usart2_rb_a2h, &usart2_rb_h2a, buf_usart2_rb_a2h,
buf_usart2_rb_h2a, &usart2_app_write_event, &usart2_tx_ready },
{ USART3, &usart3_intr, INTR_REQ_USART3,
&usart3_stat, &usart3_rb_a2h, &usart3_rb_h2a, buf_usart3_rb_a2h,
buf_usart3_rb_h2a, &usart3_app_write_event, &usart3_tx_ready },
};
/* We assume 36MHz f_PCLK */
struct brr_setting {
uint8_t baud_spec;
uint16_t brr_value;
};
#define NUM_BAUD (int)(sizeof (brr_table) / sizeof (struct brr_setting))
static const struct brr_setting brr_table[] = {
{ B600, (3750 << 4)},
{ B1200, (1875 << 4)},
@@ -104,553 +92,24 @@ static const struct brr_setting brr_table[] = {
{ B230400, ( 9 << 4)|12},
{ B460800, ( 4 << 4)|14},
{ B921600, ( 2 << 4)|7},
{ BSCARD, ( 232 << 4)|8},
{ BSCARD1, ( 232 << 4)|8}, /* 9677 */
{ BSCARD2, ( 116 << 4)|4}, /* 19354 */
{ BSCARD4, ( 58 << 4)|2}, /* 38709 */
{ BSCARD8, ( 29 << 4)|1}, /* 77419 */
{ BSCARD12, ( 19 << 4)|6}, /* 116129 */
{ BSCARD16, ( 14 << 4)|9}, /* 154506 */
{ BSCARD20, ( 11 << 4)|10}, /* 193548 */
};
static void *usart_main (void *arg);
#include "usart-common.c"
static struct usart_stat usart2_stat;
static struct usart_stat usart3_stat;
void
usart_config_clken (uint8_t dev_no, int on)
static void
usart_rcc_setup (void)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (on)
USARTx->CR2 |= (1 << 11);
else
USARTx->CR2 &= ~(1 << 11);
}
int
usart_config (uint8_t dev_no, uint32_t config_bits)
{
struct USART *USARTx = get_usart_dev (dev_no);
uint8_t baud_spec = (config_bits & MASK_BAUD);
int i;
uint32_t cr1_config = (USART_CR1_UE | USART_CR1_RXNEIE
| USART_CR1_TE | USART_CR1_RE);
/* TXEIE will be enabled when putting char */
/* No CTSIE, PEIE, TCIE, IDLEIE, LBDIE */
if (USARTx == NULL)
return -1;
/* Disable USART before configure. */
USARTx->CR1 &= ~USART_CR1_UE;
if (((config_bits & MASK_CS) == CS7 && (config_bits & PARENB))
|| ((config_bits & MASK_CS) == CS8 && (config_bits & PARENB) == 0))
cr1_config &= ~USART_CR1_M;
else if ((config_bits & MASK_CS) == CS8)
cr1_config |= USART_CR1_M;
else
return -1;
if ((config_bits & PARENB) == 0)
cr1_config &= ~(USART_CR1_PCE | USART_CR1_PEIE);
else
cr1_config |= (USART_CR1_PCE | USART_CR1_PEIE);
if ((config_bits & PARODD) == 0)
cr1_config &= ~USART_CR1_PS;
else
cr1_config |= USART_CR1_PS;
if ((config_bits & MASK_STOP) == STOP0B5)
USARTx->CR2 = (0x1 << 12);
else if ((config_bits & MASK_STOP) == STOP1B)
USARTx->CR2 = (0x0 << 12);
else if ((config_bits & MASK_STOP) == STOP1B5)
USARTx->CR2 = (0x3 << 12);
else /* if ((config_bits & MASK_STOP) == STOP2B) */
USARTx->CR2 = (0x2 << 12);
for (i = 0; i < NUM_BAUD; i++)
if (brr_table[i].baud_spec == baud_spec)
break;
if (i >= NUM_BAUD)
return -1;
USARTx->BRR = brr_table[i].brr_value;
if ((config_bits & MASK_FLOW))
USARTx->CR3 = (1 << 9) | (1 << 8);
else
USARTx->CR3 = 0;
USARTx->CR1 = cr1_config;
/* SCEN (smartcard enable) should be set _after_ CR1. */
if ((config_bits & MASK_MODE))
{
if ((config_bits & MASK_MODE) == MODE_SMARTCARD)
{
USARTx->GTPR = (16 << 8) | 5;
USARTx->CR3 |= ((1 << 5) | (1 << 4));
}
else if ((config_bits & MASK_MODE) == MODE_IRDA)
USARTx->CR3 |= (1 << 1);
else if ((config_bits & MASK_MODE) == MODE_IRDA_LP)
USARTx->CR3 |= (1 << 2) | (1 << 1);
}
return 0;
}
static int (*ss_notify_callback) (uint8_t dev_no, uint16_t notify_bits);
void
usart_init (uint16_t prio, uintptr_t stack_addr, size_t stack_size,
int (*cb) (uint8_t dev_no, uint16_t notify_bits))
{
ss_notify_callback = cb;
usart2_stat.dev_no = 2;
usart3_stat.dev_no = 3;
/* Enable USART2 and USART3 clocks, and strobe reset. */
RCC->APB1ENR |= ((1 << 18) | (1 << 17));
RCC->APB1RSTR = ((1 << 18) | (1 << 17));
RCC->APB1RSTR = 0;
chopstx_create (prio, stack_addr, stack_size, usart_main, NULL);
}
/*
* Ring buffer
*/
#define MAX_RB_BUF 1024
struct rb {
uint8_t *buf;
chopstx_mutex_t m;
chopstx_cond_t data_available;
chopstx_cond_t space_available;
uint32_t head :10;
uint32_t tail :10;
uint32_t size :10;
uint32_t full : 1;
uint32_t empty : 1;
};
/*
* Note: size = 1024 can still work, regardless of the limit of 10-bit.
*/
static void
rb_init (struct rb *rb, uint8_t *p, uint16_t size)
{
rb->buf = p;
rb->size = size;
chopstx_mutex_init (&rb->m);
chopstx_cond_init (&rb->data_available);
chopstx_cond_init (&rb->space_available);
rb->head = rb->tail = 0;
rb->full = 0;
rb->empty = 1;
}
static void
rb_add (struct rb *rb, uint8_t v)
{
rb->buf[rb->tail++] = v;
if (rb->tail == rb->size)
rb->tail = 0;
if (rb->tail == rb->head)
rb->full = 1;
rb->empty = 0;
}
static uint8_t
rb_del (struct rb *rb)
{
uint32_t v = rb->buf[rb->head++];
if (rb->head == rb->size)
rb->head = 0;
if (rb->head == rb->tail)
rb->empty = 1;
rb->full = 0;
return v;
}
/*
* Application: consumer
* Hardware: generator
*/
static int
rb_ll_put (struct rb *rb, uint8_t v)
{
int r;
chopstx_mutex_lock (&rb->m);
if (rb->full)
r = -1;
else
{
r = 0;
rb_add (rb, v);
chopstx_cond_signal (&rb->data_available);
}
chopstx_mutex_unlock (&rb->m);
return r;
}
/*
* Application: generator
* Hardware: consumer
*/
static int
rb_ll_get (struct rb *rb)
{
int r;
chopstx_mutex_lock (&rb->m);
if (rb->empty)
r = -1;
else
{
r = rb_del (rb);
chopstx_cond_signal (&rb->space_available);
}
chopstx_mutex_unlock (&rb->m);
return r;
}
static void
rb_ll_flush (struct rb *rb)
{
chopstx_mutex_lock (&rb->m);
while (!rb->empty)
rb_del (rb);
chopstx_cond_signal (&rb->space_available);
chopstx_mutex_unlock (&rb->m);
}
/*
* Application: consumer
* Hardware: generator
*/
static int
rb_read (struct rb *rb, uint8_t *buf, uint16_t buflen)
{
int i = 0;
chopstx_mutex_lock (&rb->m);
while (rb->empty)
chopstx_cond_wait (&rb->data_available, &rb->m);
while (i < buflen)
{
buf[i++] = rb_del (rb);
if (rb->empty)
break;
}
chopstx_cond_signal (&rb->space_available);
chopstx_mutex_unlock (&rb->m);
return i;
}
/*
* Application: generator
* Hardware: consumer
*/
static void
rb_write (struct rb *rb, uint8_t *buf, uint16_t buflen)
{
int i = 0;
chopstx_mutex_lock (&rb->m);
do
{
while (rb->full)
chopstx_cond_wait (&rb->space_available, &rb->m);
while (i < buflen)
{
rb_add (rb, buf[i++]);
if (rb->full)
{
chopstx_cond_signal (&rb->data_available);
break;
}
}
}
while (i < buflen);
if (i)
chopstx_cond_signal (&rb->data_available);
chopstx_mutex_unlock (&rb->m);
}
static int
rb_empty_check (void *arg)
{
struct rb *rb = arg;
return rb->empty == 0;
}
static void
rb_get_prepare_poll (struct rb *rb, chopstx_poll_cond_t *poll_desc)
{
poll_desc->type = CHOPSTX_POLL_COND;
poll_desc->ready = 0;
poll_desc->cond = &rb->data_available;
poll_desc->mutex = &rb->m;
poll_desc->check = rb_empty_check;
poll_desc->arg = rb;
}
#define INTR_REQ_USART2 38
#define INTR_REQ_USART3 39
static uint8_t buf_usart2_rb_a2h[256];
static uint8_t buf_usart2_rb_h2a[512];
static uint8_t buf_usart3_rb_a2h[256];
static uint8_t buf_usart3_rb_h2a[512];
static struct chx_intr usart2_intr;
static struct chx_intr usart3_intr;
static struct rb usart2_rb_a2h;
static struct rb usart2_rb_h2a;
static struct rb usart3_rb_a2h;
static struct rb usart3_rb_h2a;
static chopstx_poll_cond_t usart2_app_write_event;
static chopstx_poll_cond_t usart3_app_write_event;
static struct chx_poll_head *usart_poll[4];
/* Global variables so that it can be easier to debug. */
static int usart2_tx_ready;
static int usart3_tx_ready;
#define UART_STATE_BITMAP_RX_CARRIER (1 << 0)
#define UART_STATE_BITMAP_TX_CARRIER (1 << 1)
#define UART_STATE_BITMAP_BREAK (1 << 2)
#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3)
#define UART_STATE_BITMAP_FRAMING (1 << 4)
#define UART_STATE_BITMAP_PARITY (1 << 5)
#define UART_STATE_BITMAP_OVERRUN (1 << 6)
static int
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
{
int tx_ready = 0;
uint32_t r = USARTx->SR;
int notify_bits = 0;
if ((r & USART_SR_TXE))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TXEIE;
}
if ((r & USART_SR_RXNE))
{
uint32_t data = USARTx->DR;
/* DR register should be accessed even if data is not used.
* Its read-access has side effect of clearing error flags.
*/
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_SR_NE))
stat->err_rx_noise++;
else if ((r & USART_SR_FE))
{
/* NOTE: Noway to distinguish framing error and break */
stat->rx_break++;
notify_bits |= UART_STATE_BITMAP_BREAK;
}
else if ((r & USART_SR_PE))
{
stat->err_rx_parity++;
notify_bits |= UART_STATE_BITMAP_PARITY;
}
else
{
if ((r & USART_SR_ORE))
{
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
stat->err_rx_overflow++;
else
stat->rx++;
}
}
else if ((r & USART_SR_ORE))
{ /* Clear ORE */
uint32_t data = USARTx->DR;
asm volatile ("" : : "r" (data) : "memory");
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
if (notify_bits)
{
if ((*ss_notify_callback) (stat->dev_no, notify_bits))
stat->err_notify_overflow++;
}
return tx_ready;
}
static int
handle_tx_ready (struct USART *USARTx, struct rb *rb2h,
struct usart_stat *stat)
{
int tx_ready = 1;
int c = rb_ll_get (rb2h);
if (c >= 0)
{
uint32_t r;
USARTx->DR = (c & 0xff);
stat->tx++;
r = USARTx->SR;
if ((r & USART_SR_TXE) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TXEIE;
}
}
return tx_ready;
}
static void *
usart_main (void *arg)
{
(void)arg;
usart2_tx_ready = 1;
usart3_tx_ready = 1;
chopstx_claim_irq (&usart2_intr, INTR_REQ_USART2);
chopstx_claim_irq (&usart3_intr, INTR_REQ_USART3);
rb_init (&usart2_rb_a2h, buf_usart2_rb_a2h, sizeof buf_usart2_rb_a2h);
rb_init (&usart2_rb_h2a, buf_usart2_rb_h2a, sizeof buf_usart2_rb_h2a);
rb_init (&usart3_rb_a2h, buf_usart3_rb_a2h, sizeof buf_usart3_rb_a2h);
rb_init (&usart3_rb_h2a, buf_usart3_rb_h2a, sizeof buf_usart3_rb_h2a);
rb_get_prepare_poll (&usart2_rb_a2h, &usart2_app_write_event);
rb_get_prepare_poll (&usart3_rb_a2h, &usart3_app_write_event);
while (1)
{
int n = 0;
usart_poll[n++] = (struct chx_poll_head *)&usart2_intr;
usart_poll[n++] = (struct chx_poll_head *)&usart3_intr;
if (usart2_tx_ready)
usart_poll[n++] = (struct chx_poll_head *)&usart2_app_write_event;
else
usart2_app_write_event.ready = 0;
if (usart3_tx_ready)
usart_poll[n++] = (struct chx_poll_head *)&usart3_app_write_event;
else
usart3_app_write_event.ready = 0;
chopstx_poll (NULL, n, usart_poll);
if (usart2_intr.ready)
{
usart2_tx_ready = handle_intr (USART2, &usart2_rb_h2a, &usart2_stat);
chopstx_intr_done (&usart2_intr);
}
if (usart3_intr.ready)
{
usart3_tx_ready = handle_intr (USART3, &usart3_rb_h2a, &usart3_stat);
chopstx_intr_done (&usart3_intr);
}
if (usart2_tx_ready && usart2_app_write_event.ready)
usart2_tx_ready = handle_tx_ready (USART2,
&usart2_rb_a2h, &usart2_stat);
if (usart3_tx_ready && usart3_app_write_event.ready)
usart3_tx_ready = handle_tx_ready (USART3,
&usart3_rb_a2h, &usart3_stat);
}
return NULL;
}
int
usart_read (uint8_t dev_no, char *buf, uint16_t buflen)
{
struct rb *rb;
if (dev_no == 2)
rb = &usart2_rb_h2a;
else if (dev_no == 3)
rb = &usart3_rb_h2a;
else
return -1;
if (buf == NULL && buflen == 0)
{
rb_ll_flush (rb);
return 0;
}
else
return rb_read (rb, (uint8_t *)buf, buflen);
}
int
usart_write (uint8_t dev_no, char *buf, uint16_t buflen)
{
struct rb *rb;
if (dev_no == 2)
rb = &usart2_rb_a2h;
else if (dev_no == 3)
rb = &usart3_rb_a2h;
else
return -1;
if (buf == NULL && buflen == 0)
rb_ll_flush (rb);
else
rb_write (rb, (uint8_t *)buf, buflen);
return 0;
}
const struct usart_stat *
usart_stat (uint8_t dev_no)
{
if (dev_no == 2)
return &usart2_stat;
else if (dev_no == 3)
return &usart3_stat;
else
return NULL;
}
int
usart_send_break (uint8_t dev_no)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (USARTx == NULL)
return -1;
if ((USARTx->CR1 & 0x01))
return 1; /* Busy sending break, which was requested before. */
USARTx->CR1 |= 0x01;
return 0;
}
#include "usart-common-f103.c"

534
contrib/usart-stm32l4.c Normal file
View File

@@ -0,0 +1,534 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <mcu/stm32l.h>
#include <contrib/usart.h>
#include <contrib/usart-impl.h>
#define RCC_APB1_1_USART2 (1 << 17)
#define RCC_APB2_USART1 (1 << 14)
/* Hardware registers */
struct USART {
volatile uint32_t CR1;
volatile uint32_t CR2;
volatile uint32_t CR3;
volatile uint32_t BRR;
volatile uint32_t GTPR;
volatile uint32_t RTOR;
volatile uint32_t RQR;
volatile uint32_t ISR;
volatile uint32_t ICR;
volatile uint32_t RDR;
volatile uint32_t TDR;
};
#define USART1_BASE (APB2PERIPH_BASE + 0x3800)
#define USART1 ((struct USART *)USART1_BASE)
#define USART2_BASE (APB1PERIPH_BASE + 0x4400)
#define USART2 ((struct USART *)USART2_BASE)
#define USART_ISR_CTS (1 << 10)
#define USART_ISR_LBDF (1 << 8)
#define USART_ISR_TXE (1 << 7)
#define USART_ISR_TC (1 << 6)
#define USART_ISR_RXNE (1 << 5)
#define USART_ISR_IDLE (1 << 4)
#define USART_ISR_ORE (1 << 3)
#define USART_ISR_NE (1 << 2)
#define USART_ISR_FE (1 << 1)
#define USART_ISR_PE (1 << 0)
#define USART_CR1_M1 (1 << 28)
#define USART_CR1_M0 (1 << 12)
#define USART_CR1_WAKE (1 << 11)
#define USART_CR1_PCE (1 << 10)
#define USART_CR1_PS (1 << 9)
#define USART_CR1_PEIE (1 << 8)
#define USART_CR1_TXEIE (1 << 7)
#define USART_CR1_TCIE (1 << 6)
#define USART_CR1_RXNEIE (1 << 5)
#define USART_CR1_IDLEIE (1 << 4)
#define USART_CR1_TE (1 << 3)
#define USART_CR1_RE (1 << 2)
#define USART_CR1_UESM (1 << 1)
#define USART_CR1_UE (1 << 0)
#define USART_CR3_CTSE (1 << 9)
#define USART_CR3_RTSE (1 << 8)
#define USART_CR3_SCEN (1 << 5)
#define USART_CR3_NACK (1 << 4)
static struct usart_stat usart2_stat;
static struct chx_intr usart1_intr;
static struct chx_intr usart2_intr;
#define BUF_A2H_SIZE 256
#define BUF_H2A_SIZE 512
static uint8_t buf_usart2_rb_a2h[BUF_A2H_SIZE];
static uint8_t buf_usart2_rb_h2a[BUF_H2A_SIZE];
static struct rb usart2_rb_a2h;
static struct rb usart2_rb_h2a;
static chopstx_poll_cond_t usart2_app_write_event;
/* Global variables so that it can be easier to debug. */
static int usart2_tx_ready;
#define INTR_REQ_USART1 37
#define INTR_REQ_USART2 38
#define USART_DEVNO_START 1
#define USART_DEVNO_END 2
static const struct usart usart_array[] =
{
{ USART1, &usart1_intr, INTR_REQ_USART1,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL,
},
{ USART2, &usart2_intr, INTR_REQ_USART2,
&usart2_stat, &usart2_rb_a2h, &usart2_rb_h2a, buf_usart2_rb_a2h,
buf_usart2_rb_h2a, &usart2_app_write_event, &usart2_tx_ready,
},
};
/* We assume 40MHz f_CK */
static const struct brr_setting brr_table[] = {
{ B600, 66667 },
{ B1200, 33333 },
{ B2400, 16667 },
{ B9600, 4167 },
{ B19200, 2083 },
{ B57600, 694 },
{ B115200, 347 },
{ B230400, 174 },
{ B460800, 87 },
{ B921600, 43 },
{ BSCARD1, 3720 },
{ BSCARD2, 1860 },
{ BSCARD4, 930 },
{ BSCARD8, 465 },
{ BSCARD12, 310 },
{ BSCARD16, 233 },
{ BSCARD20, 186 },
};
#include "usart-common.c"
static void
usart_config_recv_enable (struct USART *USARTx, int on)
{
if (on)
{
USARTx->CR1 |= USART_CR1_RE;
/* Wait for REACK bit. */
while ((USARTx->ISR & (1 << 22)) == 0)
;
}
else
USARTx->CR1 &= ~USART_CR1_RE;
}
int
usart_config (uint8_t dev_no, uint32_t config_bits)
{
struct USART *USARTx = get_usart_dev (dev_no);
uint8_t baud_spec = (config_bits & MASK_BAUD);
int i;
uint32_t cr1_config = (USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE);
/* TXEIE/TCIE will be enabled when
putting char */
/* No CTSIE, PEIE, IDLEIE, LBDIE */
if (USARTx == NULL)
return -1;
/* Disable USART before configure. */
USARTx->CR1 &= ~USART_CR1_UE;
if ((config_bits & MASK_CS) == CS7 && !(config_bits & PARENB))
cr1_config |= USART_CR1_M1;
else if (((config_bits & MASK_CS) == CS7 && (config_bits & PARENB))
|| ((config_bits & MASK_CS) == CS8 && (config_bits & PARENB) == 0))
;
else if ((config_bits & MASK_CS) == CS8)
cr1_config |= USART_CR1_M0;
else
return -1;
if ((config_bits & PARENB))
cr1_config |= (USART_CR1_PCE | USART_CR1_PEIE);
if ((config_bits & PARODD))
cr1_config |= USART_CR1_PS;
if ((config_bits & MASK_STOP) == STOP0B5)
USARTx->CR2 = (0x1 << 12);
else if ((config_bits & MASK_STOP) == STOP1B)
USARTx->CR2 = (0x0 << 12);
else if ((config_bits & MASK_STOP) == STOP1B5)
USARTx->CR2 = (0x3 << 12);
else /* if ((config_bits & MASK_STOP) == STOP2B) */
USARTx->CR2 = (0x2 << 12);
for (i = 0; i < NUM_BAUD; i++)
if (brr_table[i].baud_spec == baud_spec)
break;
if (i >= NUM_BAUD)
return -1;
USARTx->BRR = brr_table[i].brr_value;
if ((config_bits & MASK_FLOW))
USARTx->CR3 = USART_CR3_CTSE | USART_CR3_RTSE;
else
USARTx->CR3 = 0;
if ((config_bits & MASK_MODE))
{
if ((config_bits & MASK_MODE) == MODE_SMARTCARD)
{
USARTx->GTPR = (1 << 8) | 5;
USARTx->CR3 |= (USART_CR3_SCEN | USART_CR3_NACK);
}
else if ((config_bits & MASK_MODE) == MODE_IRDA)
USARTx->CR3 |= (1 << 1);
else if ((config_bits & MASK_MODE) == MODE_IRDA_LP)
USARTx->CR3 |= (1 << 2) | (1 << 1);
}
else
cr1_config |= USART_CR1_RE;
USARTx->CR1 = cr1_config;
/* Wait for TEACK bit. */
while ((USARTx->ISR & (1 << 21)) == 0)
;
return 0;
}
void
usart_init0 (int (*cb) (uint8_t dev_no, uint16_t notify_bits))
{
int i;
ss_notify_callback = cb;
for (i = 0; i < NUM_USART; i++)
{
if (usart_array[i].stat)
usart_array[i].stat->dev_no = i + USART_DEVNO_START;
chopstx_claim_irq (usart_array[i].intr, usart_array[i].irq_num);
}
/* Enable USART1 clock, and strobe reset. */
RCC->APB2ENR |= RCC_APB2_USART1;
RCC->APB2RSTR = RCC_APB2_USART1;
RCC->APB2RSTR = 0;
/* Enable USART2 clock, and strobe reset. */
RCC->APB1ENR1 |= RCC_APB1_1_USART2;
RCC->APB1RSTR1 = RCC_APB1_1_USART2;
RCC->APB1RSTR1 = 0;
}
#define UART_STATE_BITMAP_RX_CARRIER (1 << 0)
#define UART_STATE_BITMAP_TX_CARRIER (1 << 1)
#define UART_STATE_BITMAP_BREAK (1 << 2)
#define UART_STATE_BITMAP_RINGSIGNAL (1 << 3)
#define UART_STATE_BITMAP_FRAMING (1 << 4)
#define UART_STATE_BITMAP_PARITY (1 << 5)
#define UART_STATE_BITMAP_OVERRUN (1 << 6)
static int
handle_intr (struct USART *USARTx, struct rb *rb2a, struct usart_stat *stat)
{
int tx_ready = 0;
uint32_t r = USARTx->ISR;
int notify_bits = 0;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
if (smartcard_mode)
{
if ((r & USART_ISR_TC))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TCIE;
}
}
else
{
if ((r & USART_ISR_TXE))
{
tx_ready = 1;
USARTx->CR1 &= ~USART_CR1_TXEIE;
}
}
if ((r & USART_ISR_RXNE))
{
uint32_t data = USARTx->RDR;
/* RDR register should be accessed even if data is not used. */
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_ISR_NE))
{
USARTx->ICR |= (1 << 2);
stat->err_rx_noise++;
}
else if ((r & USART_ISR_FE))
{
/* NOTE: Noway to distinguish framing error and break */
USARTx->ICR |= (1 << 1);
stat->rx_break++;
notify_bits |= UART_STATE_BITMAP_BREAK;
}
else if ((r & USART_ISR_PE))
{
USARTx->ICR |= (1 << 0);
stat->err_rx_parity++;
notify_bits |= UART_STATE_BITMAP_PARITY;
}
else
{
if ((r & USART_ISR_ORE))
{
USARTx->ICR |= (1 << 3);
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
/* XXX: if CS is 7-bit, mask it, or else parity bit in upper layer */
if (rb_ll_put (rb2a, (data & 0xff)) < 0)
stat->err_rx_overflow++;
else
stat->rx++;
}
}
else if ((r & USART_ISR_ORE))
{ /* Clear ORE */
USARTx->ICR |= (1 << 3);
stat->err_rx_overrun++;
notify_bits |= UART_STATE_BITMAP_OVERRUN;
}
if (notify_bits)
{
if (ss_notify_callback
&& (*ss_notify_callback) (stat->dev_no, notify_bits))
stat->err_notify_overflow++;
}
return tx_ready;
}
static int
handle_tx (struct USART *USARTx, struct rb *rb2h, struct usart_stat *stat)
{
int tx_ready = 1;
int c = rb_ll_get (rb2h);
if (c >= 0)
{
uint32_t r;
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
USARTx->TDR = (c & 0xff);
stat->tx++;
r = USARTx->ISR;
if (smartcard_mode)
{
if ((r & USART_ISR_TC) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TCIE;
}
}
else
{
if ((r & USART_ISR_TXE) == 0)
{
tx_ready = 0;
USARTx->CR1 |= USART_CR1_TXEIE;
}
}
}
return tx_ready;
}
int
usart_send_break (uint8_t dev_no)
{
struct USART *USARTx = get_usart_dev (dev_no);
if (USARTx == NULL)
return -1;
if ((USARTx->ISR & (1 << 18)))
return 1; /* Busy sending break, which was requested before. */
/* ??? Should we check TX is empty? */
USARTx->RQR |= 0x02;
return 0;
}
int
usart_block_sendrecv (uint8_t dev_no, const char *s_buf, uint16_t s_buflen,
char *r_buf, uint16_t r_buflen,
uint32_t *timeout_block_p, uint32_t timeout_char)
{
uint32_t timeout;
uint8_t *p;
int len;
uint32_t r;
uint32_t data;
struct USART *USARTx = get_usart_dev (dev_no);
int smartcard_mode = ((USARTx->CR3 & USART_CR3_SCEN) != 0);
struct chx_intr *usartx_intr = get_usart_intr (dev_no);
struct chx_poll_head *ph[1];
if (usartx_intr == NULL)
return -1;
ph[0] = (struct chx_poll_head *)usartx_intr;
p = (uint8_t *)s_buf;
if (p)
{
if (smartcard_mode)
usart_config_recv_enable (USARTx, 0);
USARTx->CR1 |= USART_CR1_TXEIE;
/* Sending part */
while (1)
{
chopstx_poll (NULL, 1, ph);
r = USARTx->ISR;
/* Here, ignore recv error(s). */
if ((r & USART_ISR_RXNE))
{
data = USARTx->RDR;
asm volatile ("" : : "r" (data) : "memory");
USARTx->ICR |= ((1 << 2) | (1 << 1) | (1 << 0));
}
else if ((r & USART_ISR_ORE))
{
USARTx->ICR |= (1 << 3);
}
if ((r & USART_ISR_TXE))
{
if (s_buflen == 0)
break;
else
{
/* Keep TXEIE bit */
USARTx->TDR = *p++;
s_buflen--;
}
}
chopstx_intr_done (usartx_intr);
}
USARTx->CR1 &= ~USART_CR1_TXEIE;
if (smartcard_mode)
{
if (timeout_block_p && (*timeout_block_p))
do
r = USARTx->ISR;
while (((r & USART_ISR_TC) == 0));
usart_config_recv_enable (USARTx, 1);
if (timeout_block_p && *timeout_block_p == 0)
{
/* Ignoring the echo back. */
do
r = USARTx->ISR;
while (((r & USART_ISR_TC) == 0));
if ((r & USART_ISR_RXNE))
{
data = USARTx->RDR;
asm volatile ("" : : "r" (data) : "memory");
}
*timeout_block_p = timeout_char;
}
}
chopstx_intr_done (usartx_intr);
}
if (r_buf == NULL)
return 0;
if (!p)
if (smartcard_mode)
usart_config_recv_enable (USARTx, 1);
/* Receiving part */
r = chopstx_poll (timeout_block_p, 1, ph);
if (r == 0)
return 0;
p = (uint8_t *)r_buf;
len = 0;
while (1)
{
r = USARTx->ISR;
data = USARTx->RDR;
asm volatile ("" : : "r" (data) : "memory");
if ((r & USART_ISR_RXNE))
{
if ((r & USART_ISR_NE) || (r & USART_ISR_FE) || (r & USART_ISR_PE))
{
/* ignore error, for now. XXX: ss_notify */
/* Clear the error flag(s) */
USARTx->ICR |= ((1 << 2) | (1 << 1) | (1 << 0));
}
else
{
*p++ = (data & 0xff);
len++;
r_buflen--;
if (r_buflen == 0)
{
chopstx_intr_done (usartx_intr);
break;
}
}
}
else if ((r & USART_ISR_ORE))
{
/* ignore error, for now. XXX: ss_notify */
/* Clear the error flag */
USARTx->ICR |= (1 << 3);
}
chopstx_intr_done (usartx_intr);
timeout = timeout_char;
r = chopstx_poll (&timeout, 1, ph);
if (r == 0)
break;
}
return len;
}

View File

@@ -11,7 +11,13 @@
#define B230400 26
#define B460800 27
#define B921600 28
#define BSCARD 63
#define BSCARD1 57
#define BSCARD2 58
#define BSCARD4 59
#define BSCARD8 60
#define BSCARD12 61
#define BSCARD16 62
#define BSCARD20 63
#define MASK_BAUD 0x3f
/* POSIX supports 5, 6. USB suppots 16 */
@@ -73,3 +79,12 @@ int usart_write (uint8_t dev_no, char *buf, uint16_t buflen);
const struct usart_stat *usart_stat (uint8_t dev_no);
int usart_send_break (uint8_t dev_no);
void usart_config_clken (uint8_t dev_no, int on);
int usart_config_baud (uint8_t dev_no, uint8_t baud_spec);
void usart_read_prepare_poll (uint8_t dev_no, chopstx_poll_cond_t *poll_desc);
int usart_read_ext (uint8_t dev_no, char *buf, uint16_t buflen, uint32_t *timeout_p);
void usart_init0 (int (*cb) (uint8_t dev_no, uint16_t notify_bits));
int usart_block_sendrecv (uint8_t dev_no, const char *s_buf, uint16_t s_buflen,
char *r_buf, uint16_t r_buflen,
uint32_t *timeout_block_p, uint32_t timeout_char);

View File

@@ -185,7 +185,8 @@ Returns old state which is 0 when it was enabled.
@subheading chopstx_poll
@anchor{chopstx_poll}
@deftypefun {int} {chopstx_poll} (uint32_t * @var{usec_p}, int @var{n}, struct chx_poll_head *const [] @var{pd_array})
@var{usec_p}: Pointer to usec for timeout. Forever if NULL.
@var{usec_p}: Pointer to usec for timeout. Forever if NULL. It is
updated on return
@var{n}: Number of poll descriptors
@@ -229,3 +230,10 @@ clock.
Return previous value of @var{enable_sleep}.
@end deftypefun
@subheading chopstx_critical
@anchor{chopstx_critical}
@deftypefun {void *} {chopstx_critical} (void *func (void * @var{})
Run the function @var{func} with @var{arg} under CPU schedule lock.
Return void * pointer.
@end deftypefun

View File

@@ -1,7 +1,7 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename chopstx.info
@set VERSION 1.14
@set VERSION 2.2
@settitle Chopstx Reference Manual
@c Unify some of the indices.
@syncodeindex tp fn
@@ -11,7 +11,7 @@
This manual is for Chopstx (version @value{VERSION}).
@noindent
Copyright @copyright{} 2013, 2015, 2016, 2017, 2018 Flying Stone Technology @*
Copyright @copyright{} 2013, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Flying Stone Technology @*
@quotation
Permission is granted to copy, distribute and/or modify this document
@@ -60,10 +60,11 @@ section entitled ``Copying''.
@menu
* Introduction:: What is Chopstx.
* Threads and only Threads:: Threads and only Threads.
* Poll or Pole:: Poll or Pole.
* Note (Use of sleep mode):: Use it carefully.
* Poll or Pole::
* Use of sleep mode:: Use it carefully.
* Compile-time macro:: Macro to be defined.
* API:: API.
* Memorandom:: Memorandom for the implementation.
Appendix
@@ -88,8 +89,10 @@ Indexes
@chapter Introduction
Chopstx is an RT thread library for ARM Cortex-M0, Cortex-M0plus,
Cortex-M3 and GNU/Linux emulation. Specifically, it is used for
STM32F030, MKL27Z, STM32F103, GD32F103 and as a command on GNU/Linux.
Cortex-M3, Cortex-M4 with no FPU or DSP, RISC-V Bumblebee, and
GNU/Linux emulation. Specifically, it is used for STM32F030, MKL27Z,
STM32F103, GD32F103, STM32L432, GD32VF103 and as a command on
GNU/Linux.
While most RTOSes come with many features, drivers, and stacks,
Chopstx just offers an RT thread library.
@@ -99,7 +102,7 @@ enables coherent code for ease of maintenance.
While threads are important, we don't need more threads than
necessary. Chopstx provides a feature of poll, so that we can
minimize use of threads.
handle multiple events by a single thread.
@node Threads and only Threads
@@ -107,7 +110,7 @@ minimize use of threads.
Chopstx doesn't use the feature of (prioritized) nested vector
interrupt mechanism at all. All interrupts are equally handled by a
single entry of chx_handle_intr which just wakes up corresponding
single entry of chx_handle_intr which just wakes up a corresponding
thread. This is the feature of Chopstx.
Nested vector interrupt machanism would be useful for interrupt-driven
@@ -123,7 +126,7 @@ done by interrupt handler, bottom half, and thead is crucial for
applications' performance. And because the demarcation should be done
at an early stage of an application development, it has a tendency,
many parts are getting demanding higher priority. Amount of code for
higher priority interrupt hander is getting bigger and bigger, while
higher priority interrupt handler is getting bigger and bigger, while
losing performance.
On the other hand, ``Threads (and only Threads)'' programming style
@@ -145,8 +148,8 @@ programming style, with minimum number of threads, avoiding
complicated dependency between threads.
@node Note (Use of sleep mode)
@chapter Note (Use of sleep mode)
@node Use of sleep mode
@chapter Use of sleep mode
Calling the chopstx_conf_idle function (> 0) to allow the idle thread
going to sleep. MCU will be in sleep mode when no threads are
@@ -178,6 +181,90 @@ Running CPU clock in MHz. Used for chopstx_usec_wait.
@include chopstx-api.texi
@node Memorandom
@chapter Memorandom for the implementation
@menu
* Honourable poverty:: Wabi and Sabi.
* Better interrupt handling::
* Static and deterministic when possible::
@end menu
@node Honourable poverty
@section Honourable poverty
Chopstx is an effort against many features. It encourages doing
harder decision earlier (because of less features).
Along with Moore's law, MCU implementations and their software are
getting more complex. It's been getting more flexibile, versatile,
and powerful.
Here is a question: the bigger is the better?
Historically, it used to be ``Yes, and it's even cheaper!''. And, for
a while, this trend continues.
However, in my opinion, it has been arrived to the point where
complexity matters. Now, it's more difficult to manage the
complexity.
With full of resources, it became possible deferring difficult
hardware or lower-level decisions to upper layer, by supporting both
ways, when we have a choice. It used to be considered a good
practice.
But, eventually, as a system, it may result many knobs, full of
options, which might be too difficult to manage.
In this situation, against existing practice, Chopstx is a challenge
to handle all food by only two wooden sticks. It's not fork and exec
nor forks and knives.
In Japan, it is common among families, to have private chopsticks for
each individual at home. It's like: these chopsticks are father's,
these chopsticks are mother's... son's and daughter's.
I hope Chopstx is the one for you.
@node Better interrupt handling
@section Better interrupt handling
In Chopstx, all interrupt handling is done by a single routine named
chx_handle_intr. It uses linear list search to find a thread which
handles the interrupt. In the fixed vector_table, we see many of
chx_handle_intr entries.
Obviously, this is suboptimal. It kills the hardware effort to
decrease interrupt latency.
It is certainly possible to support configurable vector table and/or
better dispatch.
The reason why I keep this badness is that I believe that when
interrupt latency matters (to the level of supporting larger vector
table, despite only few cycles elimination), something is going wrong.
You should have better solution or work around, instead of eliminating
few cycles in an interrupt handler.
When I have an opportunity to design MCU, I don't support larger
interrupt vector table.
@node Static and deterministic when possible
@section Static and deterministic when possible
When an application enables features dynamically, it may invite
non-deterministic bugs. Typical example: the order of driver
initialization matters, because of hidden dependency in a hardware
implementation. To cover all the cases, tests needed can become huge.
A simple practice like following is good when it's enough: doing all
initialization at start, then running threads to work.
If possible, it's better to avoid supporting fine grain power control
and/or dynamic clock frequency change.
@c ********************************************

View File

@@ -1,7 +1,7 @@
/*
* entry.c - Entry routine when reset and interrupt vectors.
* entry-cortex-m.c - Entry routine when reset and interrupt vectors.
*
* Copyright (C) 2013, 2014, 2015, 2016, 2017
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2019, 2021
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -23,30 +23,16 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <chopstx-cortex-m.h>
#include "board.h"
#ifdef GNU_LINUX_EMULATION
int emulated_main (int, const char **);
void chx_init (struct chx_thread *);
void chx_systick_init (void);
extern struct chx_thread main_thread;
int
main (int argc, const char *argv[])
{
chx_init (&main_thread);
chx_systick_init ();
emulated_main (argc, argv);
}
#else
#if defined(USE_SYS3) || defined(USE_SYS_CLOCK_GPIO_SETTING)
#define REQUIRE_CLOCK_GPIO_SETTING_IN_SYS
#include "sys.h"
@@ -54,12 +40,16 @@ main (int argc, const char *argv[])
* Avoid medium density specific code and prepare for high density
* device, too.
*/
#undef STM32F10X_MD
#if !defined(MCU_STM32L4)
#define STM32F10X_HD
#endif
#else
#if defined (MCU_KINETIS_L)
#include "mcu/clk_gpio_init-mkl27z.c"
#elif defined (MCU_STM32L4)
#include "mcu/clk_gpio_init-stm32l.c"
#else
#include "mcu/clk_gpio_init-stm32.c"
#include "mcu/clk_gpio_init-stm32f.c"
#endif
#endif
@@ -71,11 +61,10 @@ main (int argc, const char *argv[])
#endif
extern uint8_t __main_stack_end__;
#if defined(__ARM_ARCH_7M__)
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
extern void svc (void);
#endif
extern void preempt (void);
extern void chx_timer_expired (void);
extern void chx_handle_intr (void);
static void nmi (void)
@@ -124,7 +113,7 @@ entry (void)
{
asm volatile ("bl clock_init\n\t"
/* Clear BSS section. */
"mov r0, #0\n\t"
"movs r0, #0\n\t"
"ldr r1, =_bss_start\n\t"
"ldr r2, =_bss_end\n"
"0:\n\t"
@@ -132,7 +121,7 @@ entry (void)
"beq 1f\n\t"
#if defined(__ARM_ARCH_6M__)
"str r0, [r1]\n\t"
"add r1, #4\n\t"
"adds r1, #4\n\t"
#else
"str r0, [r1], #4\n\t"
#endif
@@ -148,8 +137,8 @@ entry (void)
#if defined(__ARM_ARCH_6M__)
"ldr r0, [r3]\n\t"
"str r0, [r1]\n\t"
"add r3, #4\n\t"
"add r1, #4\n\t"
"adds r3, #4\n\t"
"adds r1, #4\n\t"
#else
"ldr r0, [r3], #4\n\t"
"str r0, [r1], #4\n\t"
@@ -158,17 +147,17 @@ entry (void)
"3:\n\t"
/* Switch to PSP. */
"ldr r0, =__process0_stack_end__\n\t"
COMPOSE_STATEMENT ("sub r0, #", CHOPSTX_THREAD_SIZE, "\n\t")
COMPOSE_STATEMENT ("subs r0, #", CHOPSTX_THREAD_SIZE, "\n\t")
"msr PSP, r0\n\t" /* Process (main routine) stack. */
"mov r1, #2\n\t"
"movs r1, #2\n\t"
"msr CONTROL, r1\n\t"
"isb\n\t"
"bl chx_init\n\t"
"bl chx_systick_init\n\t"
"bl gpio_init\n\t"
/* Enable interrupts. */
#if defined(__ARM_ARCH_7M__)
"mov r0, #0\n\t"
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
"movs r0, #0\n\t"
"msr BASEPRI, r0\n\t"
#endif
"cpsie i\n\t"
@@ -197,13 +186,13 @@ handler vector_table[] __attribute__ ((section(".startup.vectors"))) = {
none, none, none, /* reserved */
#if defined(__ARM_ARCH_6M__)
none, /* SVCall */
#elif defined(__ARM_ARCH_7M__)
#elif defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
svc, /* SVCall */
#endif
none, /* Debug */
none, /* reserved */
preempt, /* PendSV */
chx_timer_expired, /* SysTick */
none, /* PendSV */
chx_handle_intr, /* SysTick */
/* 0x40 */
chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */,
chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */,
@@ -233,12 +222,24 @@ handler vector_table[] __attribute__ ((section(".startup.vectors"))) = {
chx_handle_intr /* EXT15_10 */, chx_handle_intr /* RTCAlarm */,
chx_handle_intr /* USBWakeup */, chx_handle_intr,
#endif
#if !defined(STM32F10X_MD)
#if defined(STM32F10X_HD)
/* High-density chips have more; ... DMA2_Channel4_5 */
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
#elif defined(MCU_STM32L4)
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
chx_handle_intr, chx_handle_intr, chx_handle_intr, chx_handle_intr,
#endif
};
#endif

45
entry-gnu-linux.c Normal file
View File

@@ -0,0 +1,45 @@
/*
* entry.c - Entry routine.
*
* Copyright (C) 2017, 2019
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
int emulated_main (int, const char **);
void chx_init (struct chx_thread *);
void chx_systick_init (void);
extern struct chx_thread main_thread;
int
main (int argc, const char *argv[])
{
chx_init (&main_thread);
chx_systick_init ();
emulated_main (argc, argv);
}

84
entry-riscv32.c Normal file
View File

@@ -0,0 +1,84 @@
/*
* entry-riscv32.c - Entry routine when reset.
*
* Copyright (C) 2019
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include "board.h"
#include "mcu/clk_gpio_init-gd32vf103.c"
/*
* Startup entry point.
*/
void __attribute__ ((naked,section(".text.startup.0")))
entry (void)
{
/* Start at 0x00000000 (alias 0x08000000), interrupt masked */
asm volatile (
"li a0,0x0800000c\n\t"
"jr a0\n\t" /* Jump to physical address */
".align 2\n\t"
".option push\n"
".option norelax\n\t"
"la gp,__global_pointer$\n"
".option pop\n\t"
"la sp,__main_stack_end__\n\t"
"call clock_init\n\t"
/* Clear BSS section. */
"la a0,_bss_start\n\t"
"la a1,_bss_end\n\t"
"0:\n\t"
"beq a0,a1,1f\n"
"sw zero,(a0)\n\t"
"addi a0,a0,4\n\t"
"j 0b\n"
"1:\n\t"
/* Copy data section. */
"la a0,_textdata\n\t"
"la a1,_data\n\t"
"la a2,_edata\n"
"0:\n\t"
"beq a1,a2,1f\n\t"
"lw t0,(a0)\n\t"
"sw t0,(a1)\n\t"
"addi a0,a0,4\n\t"
"addi a1,a1,4\n\t"
"j 0b\n"
"1:\n\t"
/* Switch to application stack. */
"la sp,__process0_stack_end__-160\n\t"
"mv a0,sp\n\t"
"call chx_init\n\t"
"call chx_systick_init\n\t"
"call gpio_init\n\t"
/* Enable interrupts. */
"csrsi mstatus,8\n\t"
/* Call main. */
"call main\n\t"
"3:\n\t"
"j 3b"
: /* no output */ : /* no input */ : "memory");
}

View File

@@ -22,7 +22,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/

View File

@@ -8,6 +8,7 @@ CHOPSTX = ..
LDSCRIPT=
CSRC = sample.c usb-cdc.c command.c
ARCH=gnu-linux
CHIP=gnu-linux
USE_SYS = yes
USE_USB = yes

View File

@@ -179,7 +179,14 @@ main (int argc, const char *argv[])
if (size < 0)
goto connection_loop;
if (size == 1)
if (size == 0)
/* Timeout */
{
if (tty_send (tty, "\r\n", 2) < 0)
return 0;
break;
}
else if (size == 1)
/* Do nothing but prompt again. */
break;
else if (size)

View File

@@ -660,31 +660,8 @@ tty_main (void *arg)
struct usb_dev dev;
int e;
#if defined(OLDER_SYS_H)
/*
* Historically (before sys < 3.0), NVIC priority setting for USB
* interrupt was done in usb_lld_sys_init. Thus this code.
*
* When USB interrupt occurs between usb_lld_init (which assumes
* ISR) and chopstx_claim_irq (which clears pending interrupt),
* invocation of usb_lld_event_handler won't occur.
*
* Calling usb_lld_event_handler is no harm even if there were no
* interrupts, thus, we call it unconditionally here, just in case
* if there is a request.
*
* We can't call usb_lld_init after chopstx_claim_irq, as
* usb_lld_init does its own setting for NVIC. Calling
* chopstx_claim_irq after usb_lld_init overrides that.
*
*/
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
goto event_handle;
#else
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
#endif
while (1)
{
@@ -692,9 +669,7 @@ tty_main (void *arg)
if (usb_intr.ready)
{
uint8_t ep_num;
#if defined(OLDER_SYS_H)
event_handle:
#endif
/*
* When interrupt is detected, call usb_lld_event_handler.
* The event may be one of following:
@@ -793,7 +768,7 @@ tty_main (void *arg)
&& t->flag_send_ready)
{
uint8_t line[32];
int len = get_chars_from_ringbuffer (t, line, sizeof (len));
int len = get_chars_from_ringbuffer (t, line, sizeof (line));
if (len)
{
@@ -929,8 +904,18 @@ tty_recv (struct tty *t, char *buf, uint32_t *timeout)
chopstx_mutex_lock (&t->mtx);
r = check_rx (t);
chopstx_mutex_unlock (&t->mtx);
if (r || (timeout != NULL && *timeout == 0))
if (r)
break;
else if (timeout != NULL && *timeout == 0)
{
int i;
/* Cancel the input. */
for (i = 0; i < t->inputline_len; i++)
tty_send (t, "\x08\x20\x08", 3);
t->inputline_len = 0;
break;
}
}
chopstx_mutex_lock (&t->mtx);

View File

@@ -3,10 +3,11 @@
PROJECT = sample
CHOPSTX = ..
LDSCRIPT= sample.ld
LDSCRIPT= sample.ld.m4
CSRC = sample.c usb-cdc.c
CHIP=stm32f103
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes
USE_USB = yes
@@ -18,9 +19,9 @@ CC = $(CROSS)gcc
LD = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
MCU = cortex-m3
MCU = cortex-m4
CWARN = -Wall -Wextra -Wstrict-prototypes
DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=72
DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=80
OPT = -O3 -Os -g
LIBS =

View File

@@ -1 +1 @@
../board/board-fst-01.h
../board/board-st-nucleo-l432.h

107
example-cdc/sample.ld.m4 Normal file
View File

@@ -0,0 +1,107 @@
/*
* ST32L4 memory setup.
*/
MEMORY
{
flash : org = 0x08000000, len = 256k
ram : org = 0x20000000, len = 48k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 20k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
_text = .;
.startup : ALIGN(128) SUBALIGN(128)
{
KEEP(*(.startup.vectors))
. = ALIGN(16);
_sys = .;
. = ALIGN(16);
KEEP(*(.sys.version))
KEEP(*(.sys.board_id))
KEEP(*(.sys.board_name))
build/sys-*.o(.text)
build/sys-*.o(.text.*)
build/sys-*.o(.rodata)
build/sys-*.o(.rodata.*)
. = ALIGN(1024);
} > flash =0xffffffff
.text : ALIGN(16) SUBALIGN(16)
{
*(.text.startup.*)
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
. = ALIGN(8);
} > flash
.ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash
.ARM.exidx : {
PROVIDE(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE(__exidx_end = .);
} > flash
.eh_frame_hdr : {*(.eh_frame_hdr)} > flash
.eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash
.textalign : ONLY_IF_RO { . = ALIGN(8); } > flash
_etext = .;
_textdata = _etext;
.stacks (NOLOAD) :
{
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.data)
. = ALIGN(4);
*(.data.*)
. = ALIGN(4);
*(.ramtext)
. = ALIGN(4);
PROVIDE(_edata = .);
} > ram AT > flash
.bss :
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
} > ram
PROVIDE(end = .);
_end = .;
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

View File

@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <chopstx.h>
#include <string.h>
#include "board.h"
#include "usb_lld.h"
#include "tty.h"
@@ -671,31 +672,8 @@ tty_main (void *arg)
struct usb_dev dev;
int e;
#if defined(OLDER_SYS_H)
/*
* Historically (before sys < 3.0), NVIC priority setting for USB
* interrupt was done in usb_lld_sys_init. Thus this code.
*
* When USB interrupt occurs between usb_lld_init (which assumes
* ISR) and chopstx_claim_irq (which clears pending interrupt),
* invocation of usb_lld_event_handler won't occur.
*
* Calling usb_lld_event_handler is no harm even if there were no
* interrupts, thus, we call it unconditionally here, just in case
* if there is a request.
*
* We can't call usb_lld_init after chopstx_claim_irq, as
* usb_lld_init does its own setting for NVIC. Calling
* chopstx_claim_irq after usb_lld_init overrides that.
*
*/
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
goto event_handle;
#else
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
#endif
while (1)
{
@@ -703,9 +681,7 @@ tty_main (void *arg)
if (usb_intr.ready)
{
uint8_t ep_num;
#if defined(OLDER_SYS_H)
event_handle:
#endif
/*
* When interrupt is detected, call usb_lld_event_handler.
* The event may be one of following:
@@ -804,7 +780,7 @@ tty_main (void *arg)
&& t->flag_send_ready)
{
uint8_t line[32];
int len = get_chars_from_ringbuffer (t, line, sizeof (len));
int len = get_chars_from_ringbuffer (t, line, sizeof (line));
if (len)
{

View File

@@ -8,6 +8,7 @@ FRAUCHEKY = ../../fraucheky
LDSCRIPT=
CSRC = main.c
ARCH=gnu-linux
CHIP=gnu-linux
USE_SYS = yes
USE_USB = yes

View File

@@ -173,7 +173,6 @@ usb_main (void *arg)
(void)arg;
chopstx_claim_irq (&interrupt, INTR_REQ_USB);
usb_lld_init (&dev, FEATURE_BUS_POWERED);
goto event_handle; /* For old SYS < 3.0 */
while (1)
{
@@ -183,7 +182,6 @@ usb_main (void *arg)
{
uint8_t ep_num;
event_handle:
e = usb_lld_event_handler (&dev);
chopstx_intr_done (&interrupt);
ep_num = USB_EVENT_ENDP (e);

View File

@@ -7,6 +7,7 @@ PROJECT = sample
CHOPSTX = ..
LDSCRIPT= sample.ld
CSRC = sample.c usb-cdc.c command.c touch.c
ARCH=cortex-m
CHIP=mkl27z
USE_SYS = yes

View File

@@ -657,31 +657,8 @@ tty_main (void *arg)
struct usb_dev dev;
int e;
#if defined(OLDER_SYS_H)
/*
* Historically (before sys < 3.0), NVIC priority setting for USB
* interrupt was done in usb_lld_sys_init. Thus this code.
*
* When USB interrupt occurs between usb_lld_init (which assumes
* ISR) and chopstx_claim_irq (which clears pending interrupt),
* invocation of usb_lld_event_handler won't occur.
*
* Calling usb_lld_event_handler is no harm even if there were no
* interrupts, thus, we call it unconditionally here, just in case
* if there is a request.
*
* We can't call usb_lld_init after chopstx_claim_irq, as
* usb_lld_init does its own setting for NVIC. Calling
* chopstx_claim_irq after usb_lld_init overrides that.
*
*/
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
goto event_handle;
#else
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
#endif
while (1)
{
@@ -692,9 +669,7 @@ tty_main (void *arg)
if (usb_intr.ready)
{
uint8_t ep_num;
#if defined(OLDER_SYS_H)
event_handle:
#endif
/*
* When interrupt is detected, call usb_lld_event_handler.
* The event may be one of following:
@@ -793,7 +768,7 @@ tty_main (void *arg)
&& t->flag_send_ready)
{
uint8_t line[32];
int len = get_chars_from_ringbuffer (t, line, sizeof (len));
int len = get_chars_from_ringbuffer (t, line, sizeof (line));
if (len)
{

View File

@@ -7,6 +7,7 @@ LDSCRIPT= hacker-emblem.ld
CSRC = reset.c hh.c
ARCH=cortex-m
CHIP=stm32f0
# Hacker Emblem and "Happy Hacking!" demonstration

View File

@@ -1,7 +1,7 @@
/*
* reset.c - No system routines, but only RESET handler for STM32F030.
*
* Copyright (C) 2015 Flying Stone Technology
* Copyright (C) 2015, 2021 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* Copying and distribution of this file, with or without modification,
@@ -19,10 +19,10 @@ reset (void)
{
asm volatile ("cpsid i\n\t" /* Mask all interrupts. */
"mov r0, pc\n\t" /* r0 = PC & ~0x0fff */
"mov r1, #0x10\n\t"
"lsl r1, #8\n\t"
"sub r1, r1, #1\n\t"
"bic r0, r0, r1\n\t"
"movs r1, #0x10\n\t"
"lsls r1, #8\n\t"
"subs r1, #1\n\t"
"bics r0, r0, r1\n\t"
"ldr r2, [r0]\n\t"
"msr MSP, r2\n\t" /* Main (exception handler) stack. */
"b entry\n\t"
@@ -31,8 +31,6 @@ reset (void)
}
extern uint8_t __main_stack_end__;
extern void preempt (void);
extern void chx_timer_expired (void);
extern void chx_handle_intr (void);
static void nmi (void)
@@ -84,8 +82,8 @@ handler vector[] __attribute__ ((section(".vectors"))) = {
none, /* SVCall */
none, /* Debug */
none, /* reserved */
preempt, /* PendSV */
chx_timer_expired, /* SysTick */
none, /* PendSV */
chx_handle_intr, /* SysTick */
/* 0x40 */
chx_handle_intr /* WWDG */, chx_handle_intr /* PVD */,
chx_handle_intr /* TAMPER */, chx_handle_intr /* RTC */,

BIN
example-lcd/FSIJ.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

59
example-lcd/Makefile Normal file
View File

@@ -0,0 +1,59 @@
# Makefile for example application of Chopstx
PROJECT = sample
### Currently, it is for GD32VF103
###
### Please change lines started with '###' for STM32 Nucleo L432.
###
### Please change lines started with '###' for Cortex-M3 board.
###
### Please change lines started with '###' for Cortex-M0+ board
### (STM32F0 Discovery).
CHOPSTX = ..
LDSCRIPT= sample.ld.gd32vf103
### LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld
### LDSCRIPT= sample.ld.m3
CSRC = sample.c usb-vendor-specific.c
# CSRC += debug.c
ARCH=riscv32
CHIP=gd32vf103
### CHIP=stm32l4
### USE_SYS = yes
USE_USART = yes
USE_USB = yes
###################################
### CROSS = arm-none-eabi-
CROSS = riscv64-unknown-elf-
CC = $(CROSS)gcc
LD = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
### MCU = cortex-m4
### MCU = cortex-m3
### MCU = cortex-m0
CWARN = -Wall -Wextra -Wstrict-prototypes
DEFS = -DFREE_STANDING -DMHZ=96
### DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=80
### DEFS = -DFREE_STANDING -DUSE_SYS3 -DBUSY_LOOP -DCHX_FLAGS_MAIN=CHOPSTX_SCHED_RR
OPT = -O3 -Os -g
LIBS =
#LIBS = -lgcc
CSRC += $(CHOPSTX)/contrib/spi-st.c
####################
include ../rules.mk
board.h:
@echo Please make a symbolic link \'board.h\' to a file in ../board;
@exit 1
sys.c: board.h
distclean: clean
rm -f board.h

1
example-lcd/board.h Symbolic link
View File

@@ -0,0 +1 @@
../board/board-longan-nano.h

View File

@@ -0,0 +1,34 @@
from load_image_to_screen import Longan_LCD
from gnupg_logo_16bit_160x80 import gnupg_logo_16bit_160x80
image = gnupg_logo_16bit_160x80
pos = [ 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 12, 14, 17, 20, 23, 28, 33, 40, 47, 56 ]
background0 = [ 0x5d, 0xdf ]
background1 = [ 0x7e, 0xdf ]
def fill(i, bg0, bg1):
if (i % 2) == 0:
return bg0
else:
return bg1
def move_right(x, bg0, bg1, img):
result = []
for i in range(80):
for j in range(x):
result = result + fill(j, bg0, bg1)
result = result + (img[i*160*2:(i+1)*160*2])[0:160*2-x*2]
return result
images = [ bytes(move_right(x, background0, background1, image)) for x in pos ]
lcd = Longan_LCD()
for loop in range(10):
for i in range(len(pos)):
lcd.load_image_to_screen(images[len(pos)-1-i])
for i in range(len(pos)):
lcd.load_image_to_screen(images[i])

26
example-lcd/debug.c Normal file
View File

@@ -0,0 +1,26 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <contrib/usart.h>
#include <stdarg.h>
extern int vsnprintf(char *str, size_t size, const char *format, va_list ap);
#define USART_NO 0
/* Intentionally only adding LF */
void
debug (const char *fmt, ...)
{
char buf[256];
va_list ap;
size_t len;
va_start(ap, fmt);
len = vsnprintf (buf, sizeof buf - 1, fmt, ap);
va_end(ap);
if (len >= sizeof buf - 1)
len = sizeof buf - 2;
buf[len++] = '\n';
usart_write (USART_NO, buf, len);
}

1
example-lcd/debug.h Normal file
View File

@@ -0,0 +1 @@
void debug (const char *fmt, ...);

1611
example-lcd/fsij-logo.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

96
example-lcd/l.py Normal file
View File

@@ -0,0 +1,96 @@
#! /usr/bin/python3
# pos = [
# 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
# 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,
# 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 11,
# 12, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 28, 30, 31, 33, 35,
# 37, 40, 42, 44, 47, 50, 53, 56, 59, 63, 66, 70, 75, 79, 84, 89, 94,100,105,112,
# 118,125,133,141,149,158
# ]
pos = [
0, 1, 1, 1,
1, 2, 2, 3,
4, 6, 8, 11,
15, 20, 26, 35,
47, 63, 84, 112,
149
]
import usb.core
import usb.util
dev = usb.core.find(idVendor=0xffff, idProduct=0x0001)
if dev is None:
raise ValueError('Device not found')
# dev.set_configuration()
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
ep_out = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
ep_in = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
if ep_out is None:
raise ValueError('Endpoint to send data not found')
if ep_in is None:
raise ValueError('Endpoint to receive data not found')
color_black = [ 0, 0 ]
color0 = [ 0xff, 0x03 ]
color1 = [ 0xef, 0x7f ]
color2 = [ 0x00, 0x78 ]
color3 = [ 0xff, 0xff ]
def gen_image(x):
image = []
for i in range(80):
image = image + (color_black * x) + (color0 * (160-x))
return image
images = [ gen_image(p) for p in pos ]
# images = [ gen_image(p) for p in range(160) ]
import time
import sys
def prepare_send():
w=ep_in.read(64)
# print(w)
def finish_send():
dev.ctrl_transfer(0x41, 0x00, 0x00, 0x00, "")
# print(".", end='', flush=True)
for i in range(len(images)*8):
i0 = i % (len(images)*2)
if i0 < len(images):
x = (i % len(images))
else:
x = len(images)- 1 - (i % len(images))
# print("%d %d: %d" % (i, x, len(images[x])))
if True:
prepare_send()
ep_out.write(images[x])
finish_send()
prepare_send()
dev.ctrl_transfer(0x41, 0x00, 0x01, 0x00, "")

View File

@@ -0,0 +1,29 @@
l = [ 0b00000_000000_00000,
0b00000_000000_01111,
0b00000_000000_11111,
0b00000_011111_00000,
0b00000_011111_01111,
0b00000_011111_11111,
0b00000_111111_00000,
0b00000_111111_01111,
0b00000_111111_11111,
0b01111_000000_00000,
0b01111_000000_01111,
0b01111_000000_11111,
0b01111_011111_00000,
0b01111_011111_01111,
0b01111_011111_11111,
0b01111_111111_00000,
0b01111_111111_01111,
0b01111_111111_11111,
0b11111_000000_00000,
0b11111_000000_01111,
0b11111_000000_11111,
0b11111_011111_00000,
0b11111_011111_01111,
0b11111_011111_11111,
0b11111_111111_00000,
0b11111_111111_01111,
0b11111_111111_11111 ]
for color in l:
print("0x%04x," % color)

View File

@@ -0,0 +1,51 @@
#! /usr/bin/python3
import usb.core
import usb.util
import time
import sys
class Longan_LCD(object):
def __init__(self):
self.dev = usb.core.find(idVendor=0xffff, idProduct=0x0001)
if self.dev is None:
raise ValueError('Device not found')
cfg = self.dev.get_active_configuration()
intf = cfg[(0,0)]
self.ep_out = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
self.ep_in = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
if self.ep_out is None:
raise ValueError('Endpoint to send data not found')
if self.ep_in is None:
raise ValueError('Endpoint to receive data not found')
def prepare_send(self):
w=self.ep_in.read(64)
# print(w)
def finish_send(self):
self.dev.ctrl_transfer(0x41, 0x00, 0x00, 0x00, "")
# print(".", end='', flush=True)
def load_image_to_screen(self, image):
self.prepare_send()
self.ep_out.write(image)
self.finish_send()

View File

@@ -0,0 +1,93 @@
#! /usr/bin/python3
import usb.core
import usb.util
dev = usb.core.find(idVendor=0xffff, idProduct=0x0001)
if dev is None:
raise ValueError('Device not found')
# dev.set_configuration()
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
ep_out = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
ep_in = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
if ep_out is None:
raise ValueError('Endpoint to send data not found')
if ep_in is None:
raise ValueError('Endpoint to receive data not found')
def prepare_send():
w=ep_in.read(64)
# print(w)
def finish_send():
dev.ctrl_transfer(0x41, 0x00, 0x00, 0x00, "")
# print(".", end='', flush=True)
color0 = [ 0xff, 0x03 ]
color1 = [ 0xef, 0x7f ]
color2 = [ 0x00, 0x78 ]
color3 = [ 0xff, 0xff ]
image = color0*160*80
# + color1*160*40
image64 = color0*32
image = color1*160*80
point1600a=(color0*40+color1*40)*20
point1600b=(color1*40+color0*40)*20
point1600c=(color2*20+color3*20)*40
point1600d=(color3*20+color2*20)*40
import time
import sys
for i in range(10):
prepare_send()
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".", end='', flush=True)
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".", end='', flush=True)
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".", end='', flush=True)
ep_out.write(point1600a)
print(".", end='', flush=True)
ep_out.write(point1600b)
print(".")
finish_send()
prepare_send()
ep_out.write(point1600c*2+point1600d*2+point1600c*2+point1600d*2)
print(".")
finish_send()
prepare_send()
dev.ctrl_transfer(0x41, 0x00, 0x01, 0x00, "")

373
example-lcd/sample.c Normal file
View File

@@ -0,0 +1,373 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include "board.h"
#include <contrib/spi.h>
#include <contrib/usart.h>
#include <mcu/gd32vf103.h>
#include "vrsp.h"
void
set_led_b (void)
{
GPIOA->ODR |= (1 << 1);
GPIOA->ODR &= ~(1 << 2);
GPIOC->ODR |= (1 << 13);
}
void
set_led_g (void)
{
GPIOA->ODR &= ~(1 << 1);
GPIOA->ODR |= (1 << 2);
GPIOC->ODR |= (1 << 13);
}
void
set_led (int on)
{
if (on)
GPIOC->ODR &= ~(1 << 13);
else
GPIOC->ODR |= (1 << 13);
}
#define STACK_MAIN
#define STACK_PROCESS_1
#include "stack-def.h"
#define PRIO_USART 3
#define STACK_ADDR_USART ((uint32_t)process1_base)
#define STACK_SIZE_USART (sizeof process1_base)
#define LCD_WIDTH 160
#define LCD_HEIGHT 80
/*
* GPIO PB1: RES#
* low active
*/
static void
lcd_reset (int assert)
{
if (assert)
GPIOB->ODR &= ~(1 << 1);
else
GPIOB->ODR |= (1 << 1);
}
/*
* GPIO PB2: CS#
* low active
*/
static void
lcd_chip_select (int assert)
{
if (assert)
GPIOB->ODR &= ~(1 << 2);
else
GPIOB->ODR |= (1 << 2);
}
/*
* GPIO PB0: D/C#
* Display: 1
* Command: 0
*/
#define LCD_DISPLAY 1
#define LCD_COMMAND 0
static void
lcd_control (int control)
{
if (control == LCD_COMMAND)
GPIOB->ODR &= ~(1 << 0);
else
GPIOB->ODR |= (1 << 0);
}
static void
lcd_send (uint8_t data)
{
spi_send (data);
spi_recv ();
}
static void
lcd_data8 (uint8_t data)
{
lcd_send (data);
}
static void
lcd_data16 (uint16_t data)
{
lcd_send (data>>8);
lcd_send (data);
}
static void
lcd_command (uint8_t cmd)
{
lcd_control (LCD_COMMAND);
lcd_send (cmd);
lcd_control (LCD_DISPLAY);
}
/* LCD: ST7735S */
static void
lcd_init (void)
{
spi_init ();
lcd_reset (1);
chopstx_usec_wait (500*1000);
lcd_reset (0);
lcd_chip_select (1);
chopstx_usec_wait (100*1000);
lcd_command (0x11); /* SLPOUT: Sleep out */
chopstx_usec_wait (100*1000);
lcd_command (0x21); /* INVON: display inversion on */
/* INVOFF: display inversion off: 0x20 */
lcd_command (0x3a); /* COLMOD: Pixel data format */
lcd_data8 (0x05); /* 16-bit/pixel */
lcd_command (0x36); /* MADCTL: Memory Data Access Control*/
lcd_data8 (0x78);
lcd_command (0xb1); /* FRMCTR1: frame rate control 1 */
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_command (0xb2); /* FRMCTR2: frame rate control 2 */
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_command (0xb3); /* FRMCTR3: frame rate control 3 */
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_data8 (0x05);
lcd_data8 (0x3a);
lcd_data8 (0x3a);
lcd_command (0xb4); /* INVCTR: inversion control */
lcd_data8 (0x03);
lcd_command (0xc0); /* PWCTR1: power control 1 */
lcd_data8 (0x62);
lcd_data8 (0x02);
lcd_data8 (0x04);
lcd_command (0xc1); /* PWCTR1: power control 2 */
lcd_data8 (0xc0);
lcd_command (0xc2); /* PWCTR1: power control 3 */
lcd_data8 (0x0d);
lcd_data8 (0x00);
lcd_command (0xc3); /* PWCTR1: power control 4 */
lcd_data8 (0x8d);
lcd_data8 (0x6a);
lcd_command (0xc4); /* PWCTR1: power control 5 */
lcd_data8 (0x8d);
lcd_data8 (0xee);
lcd_command (0xc5); /* VCMCTR1: VCOM control 1 */
lcd_data8 (0x0e);
lcd_command (0xe0); /* GMCTRP1: Gamma correction setting */
lcd_data8 (0x10);
lcd_data8 (0x0e);
lcd_data8 (0x02);
lcd_data8 (0x03);
lcd_data8 (0x0e);
lcd_data8 (0x07);
lcd_data8 (0x02);
lcd_data8 (0x07);
lcd_data8 (0x0a);
lcd_data8 (0x12);
lcd_data8 (0x27);
lcd_data8 (0x37);
lcd_data8 (0x00);
lcd_data8 (0x0d);
lcd_data8 (0x0e);
lcd_data8 (0x10);
lcd_command (0xe1); /* GMCTRN1: Gamma correction setting */
lcd_data8 (0x10);
lcd_data8 (0x0e);
lcd_data8 (0x03);
lcd_data8 (0x03);
lcd_data8 (0x0f);
lcd_data8 (0x06);
lcd_data8 (0x02);
lcd_data8 (0x08);
lcd_data8 (0x0a);
lcd_data8 (0x13);
lcd_data8 (0x26);
lcd_data8 (0x36);
lcd_data8 (0x00);
lcd_data8 (0x0d);
lcd_data8 (0x0e);
lcd_data8 (0x10);
/* All set up! Now, turn on the display. */
lcd_command (0x29); /* DISPON: Display On */
}
static void
lcd_address (uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
lcd_command (0x2a); /* CASET: Column address */
lcd_data16 (x1+1);
lcd_data16 (x2+1);
lcd_command (0x2b); /* RASET: Row address */
lcd_data16 (y1+26);
lcd_data16 (y2+26);
lcd_command (0x2c); /* RAMWR: memory write */
}
static void
lcd_clear (uint16_t color)
{
uint16_t i, j;
lcd_address (0, 0, LCD_WIDTH-1, LCD_HEIGHT-1);
for (i = 0; i < LCD_WIDTH; i++)
for (j = 0; j < LCD_HEIGHT; j++)
lcd_data16 (color);
}
static void
lcd_load_image (const uint16_t *image)
{
int i;
lcd_address (0, 0, LCD_WIDTH-1, LCD_HEIGHT-1);
for(i = 0; i < LCD_WIDTH*LCD_HEIGHT; i++)
lcd_data16 (image[i]);
}
/* Generated with list_565_colors.py */
static uint16_t colors[27] =
{
0x0000, 0x000f, 0x001f, 0x03e0, 0x03ef, 0x03ff, 0x07e0, 0x07ef, 0x07ff,
0x7800, 0x780f, 0x781f, 0x7be0, 0x7bef, 0x7bff, 0x7fe0, 0x7fef, 0x7fff,
0xf800, 0xf80f, 0xf81f, 0xfbe0, 0xfbef, 0xfbff, 0xffe0, 0xffef, 0xffff,
};
static int
ss_notify (uint8_t dev_no, uint16_t state_bits)
{
(void)dev_no;
(void)state_bits;
return 0;
}
uint16_t screen_image[12800];
int
main (int argc, const char *argv[])
{
chopstx_poll_cond_t usart_poll_desc;
chopstx_poll_cond_t usb_poll_desc;
struct chx_poll_head *ph[2];
uint32_t timeout;
int cl = 0;
int play_mode = 0;
struct vrsp *v;
uint32_t u = 0;
(void)argc;
(void)argv;
usart_init (PRIO_USART, STACK_ADDR_USART, STACK_SIZE_USART, ss_notify);
usart_config (0, B115200 | CS8 | STOP1B);
v = vrsp_open ();
chopstx_usec_wait (500*1000);
set_led (1);
lcd_init ();
lcd_clear (colors[26]);
chopstx_usec_wait (500*1000);
vrsp_prepare_poll (v, &usb_poll_desc);
ph[0] = (struct chx_poll_head *)&usb_poll_desc;
usart_read_prepare_poll (0, &usart_poll_desc);
ph[1] = (struct chx_poll_head *)&usart_poll_desc;
timeout = 200*1000*6;
while (1)
{
chopstx_poll (&timeout, 2, ph);
if (usart_poll_desc.ready)
{
char buf[16];
int r;
r = usart_read (0, buf, 16);
if (r)
usart_write (0, buf, r);
}
if (usb_poll_desc.ready)
{
int r;
r = vrsp_screen_acquire (v);
if (r < 0)
/* It's busy. */
;
else if (r == 0)
{
play_mode = 1;
lcd_load_image (screen_image);
vrsp_screen_release (v);
}
else
{
play_mode = 0;
vrsp_screen_release (v);
}
}
if (timeout == 0)
{
usart_write (0, "Hello\r\n", 7);
timeout = 200*1000*6;
u ^= 1;
set_led (u);
if (play_mode == 0)
{
lcd_clear (colors[cl]);
cl++;
if (cl >= 27)
cl = 0;
}
}
}
return 0;
}

View File

@@ -0,0 +1,92 @@
/*
* GD32VF103 memory setup.
*/
MEMORY
{
flash : org = 0x08000000, len = 128k
ram : org = 0x20000000, len = 32k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 32k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
_text = .;
.text :
{
*(.text.startup.0)
*(.text.startup.1)
*(.text.startup.*)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.rodata)
*(.rodata.*)
. = ALIGN(4);
} > flash
PROVIDE (_etext = .);
_textdata = _etext;
.stacks (NOLOAD) :
{
. = ALIGN(8);
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
*(.process_stack.4)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.gnu.linkonce.r.*)
*(.data)
. = ALIGN(4);
*(.data.*)
*(.gnu.linkonce.d.*)
. = ALIGN(8);
PROVIDE( __global_pointer$ = . + 0x800);
*(.sdata .sdata.*)
*(.gnu.linkonce.s.*)
. = ALIGN(8);
*(.srodata.cst16)
*(.srodata.cst8)
*(.srodata.cst4)
*(.srodata.cst2)
*(.srodata .srodata.*)
} > ram AT > flash
. = ALIGN(4);
PROVIDE(_edata = .);
PROVIDE(_bss_start = .);
.bss :
{
*(.sbss*)
*(.gnu.linkonce.sb.*)
*(.bss .bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
} > ram
. = ALIGN(8);
PROVIDE(_bss_end = .);
PROVIDE(end = .);
PROVIDE(_end = .);
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

55
example-lcd/stack-def.h Normal file
View File

@@ -0,0 +1,55 @@
#define MAIN_SIZE 0x0100 /* Idle+Exception handlers */
#define SIZE_0 0x0600 /* Main program */
#define SIZE_1 0x0a00 /* first thread program */
#define SIZE_2 0x0600 /* second thread program */
#define SIZE_3 0x0000 /* third thread program */
#define SIZE_4 0x0000 /* fourth thread program */
#if defined(STACK_MAIN)
/*
* The terminology of "main" is confusing in ARM architecture.
* Here, "main_base" is for exception handlers.
*/
/* Idle+Exception handlers */
char __main_stack_end__[0] __attribute__ ((section(".main_stack")));
char main_base[MAIN_SIZE] __attribute__ ((section(".main_stack")));
/* Main program */
char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0")));
char process0_base[SIZE_0] __attribute__ ((section(".process_stack.0")));
#endif
/* First thread program */
#if defined(STACK_PROCESS_1)
char process1_base[SIZE_1] __attribute__ ((section(".process_stack.1")));
#endif
/* Second thread program */
#if defined(STACK_PROCESS_2)
char process2_base[SIZE_2] __attribute__ ((section(".process_stack.2")));
#endif
/* Third thread program */
#if defined(STACK_PROCESS_3)
char process3_base[SIZE_3] __attribute__ ((section(".process_stack.3")));
#endif
/* Fourth thread program */
#if defined(STACK_PROCESS_4)
char process4_base[SIZE_4] __attribute__ ((section(".process_stack.4")));
#endif
/* Fifth thread program */
#if defined(STACK_PROCESS_5)
char process5_base[SIZE_5] __attribute__ ((section(".process_stack.5")));
#endif
/* Sixth thread program */
#if defined(STACK_PROCESS_6)
char process6_base[SIZE_6] __attribute__ ((section(".process_stack.6")));
#endif
/* Seventh thread program */
#if defined(STACK_PROCESS_7)
char process7_base[SIZE_7] __attribute__ ((section(".process_stack.7")));
#endif

View File

@@ -0,0 +1,585 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include <string.h>
#include "board.h"
#include "usb_lld.h"
#include "vrsp.h"
extern uint16_t screen_image[12800];
static chopstx_intr_t usb_intr;
struct vrsp {
struct usb_dev dev;
chopstx_mutex_t mtx;
chopstx_cond_t cnd;
uint32_t device_state : 3; /* USB device status */
uint32_t screen_mem_owner : 1; /* 0: driver, 1: app */
uint32_t mode : 8;
uint16_t *screen_ptr;
};
static struct vrsp vrsp0;
/*
* Locate VRSP structure from interface number or endpoint number.
* Currently, it always returns vrsp0, because we only have the one.
*/
static struct vrsp *
vrsp_get (int interface, uint8_t ep_num)
{
struct vrsp *v = &vrsp0;
(void)interface;
(void)ep_num;
return v;
}
static void vrsp_update_finish (struct vrsp *v, int value);
/* USB Device Descriptor */
static const uint8_t vrsp_device_desc[18] = {
18, /* bLength */
DEVICE_DESCRIPTOR, /* bDescriptorType */
0x10, 0x01, /* bcdUSB = 1.1 */
0x00, /* bDeviceClass. */
0x00, /* bDeviceSubClass. */
0x00, /* bDeviceProtocol. */
0x40, /* bMaxPacketSize. */
0xFF, 0xFF, /* idVendor */
0x01, 0x00, /* idProduct */
0x00, 0x01, /* bcdDevice */
1, /* iManufacturer. */
2, /* iProduct. */
3, /* iSerialNumber. */
1 /* bNumConfigurations. */
};
#define FEATURE_BUS_POWERED 0x80
/* Configuration Descriptor */
static const uint8_t vrsp_config_desc[32] = {
9,
CONFIG_DESCRIPTOR, /* bDescriptorType: Configuration */
/* Configuration Descriptor.*/
32, 0x00, /* wTotalLength. */
0x01, /* bNumInterfaces. */
0x01, /* bConfigurationValue. */
0, /* iConfiguration. */
FEATURE_BUS_POWERED, /* bmAttributes. */
50, /* bMaxPower (100mA). */
/* Interface Descriptor.*/
9,
INTERFACE_DESCRIPTOR,
0x00, /* bInterfaceNumber. */
0x00, /* bAlternateSetting. */
0x02, /* bNumEndpoints. */
0xFF, /* bInterfaceClass (Vendor specific). */
0x00, /* bInterfaceSubClass. */
0x00, /* bInterfaceProtocol. */
0, /* iInterface. */
/* Endpoint 1-OUT Descriptor.*/
7,
ENDPOINT_DESCRIPTOR,
ENDP1, /* bEndpointAddress. */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval. */
/* Endpoint 1-IN Descriptor.*/
7,
ENDPOINT_DESCRIPTOR,
ENDP1|0x80, /* bEndpointAddress. */
0x02, /* bmAttributes (Bulk). */
0x40, 0x00, /* wMaxPacketSize. */
0x00, /* bInterval. */
};
/*
* U.S. English language identifier.
*/
static const uint8_t vrsp_string0[4] = {
4, /* bLength */
STRING_DESCRIPTOR,
0x09, 0x04 /* LangID = 0x0409: US-English */
};
static const uint8_t vrsp_string1[] = {
23*2+2, /* bLength */
STRING_DESCRIPTOR, /* bDescriptorType */
/* Manufacturer: "Flying Stone Technology" */
'F', 0, 'l', 0, 'y', 0, 'i', 0, 'n', 0, 'g', 0, ' ', 0, 'S', 0,
't', 0, 'o', 0, 'n', 0, 'e', 0, ' ', 0, 'T', 0, 'e', 0, 'c', 0,
'h', 0, 'n', 0, 'o', 0, 'l', 0, 'o', 0, 'g', 0, 'y', 0,
};
static const uint8_t vrsp_string2[] = {
14*2+2, /* bLength */
STRING_DESCRIPTOR, /* bDescriptorType */
/* Product name: "Chopstx Sample" */
'C', 0, 'h', 0, 'o', 0, 'p', 0, 's', 0, 't', 0, 'x', 0, ' ', 0,
'S', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0,
};
/*
* Serial Number string.
*/
static const uint8_t vrsp_string3[10] = {
10, /* bLength */
STRING_DESCRIPTOR, /* bDescriptorType */
'0', 0, '.', 0, '0', 0, '0', 0, /* Version number */
};
#define NUM_INTERFACES 1
static void
usb_device_reset (struct usb_dev *dev)
{
usb_lld_reset (dev, FEATURE_BUS_POWERED);
chopstx_mutex_lock (&vrsp0.mtx);
vrsp0.device_state = USB_DEVICE_STATE_ATTACHED;
vrsp0.screen_mem_owner = 0;
vrsp0.screen_ptr = screen_image;
vrsp0.mode = 2;
chopstx_cond_signal (&vrsp0.cnd);
chopstx_mutex_unlock (&vrsp0.mtx);
}
static void
usb_ctrl_write_finish (struct usb_dev *dev)
{
(void)dev;
}
static int
usb_setup (struct usb_dev *dev)
{
struct device_req *arg = &dev->dev_req;
uint8_t type_rcp = arg->type & (REQUEST_TYPE|RECIPIENT);
if (USB_SETUP_SET (arg->type))
{
if (type_rcp == (VENDOR_REQUEST | INTERFACE_RECIPIENT)
&& arg->request == 0x00
&& arg->index == 0 /* interface 0 */)
{
struct vrsp *v = vrsp_get (arg->index, 0);
/* Finish the screen update. */
vrsp_update_finish (v, arg->value);
return usb_lld_ctrl_ack (dev);
}
}
return -1;
}
static int
usb_get_descriptor (struct usb_dev *dev)
{
struct device_req *arg = &dev->dev_req;
uint8_t rcp = arg->type & RECIPIENT;
uint8_t desc_type = (arg->value >> 8);
uint8_t desc_index = (arg->value & 0xff);
if (rcp != DEVICE_RECIPIENT)
return -1;
if (desc_type == DEVICE_DESCRIPTOR)
return usb_lld_ctrl_send (dev,
vrsp_device_desc, sizeof (vrsp_device_desc));
else if (desc_type == CONFIG_DESCRIPTOR)
return usb_lld_ctrl_send (dev,
vrsp_config_desc, sizeof (vrsp_config_desc));
else if (desc_type == STRING_DESCRIPTOR)
{
const uint8_t *str;
int size;
switch (desc_index)
{
case 0:
str = vrsp_string0;
size = sizeof (vrsp_string0);
break;
case 1:
str = vrsp_string1;
size = sizeof (vrsp_string1);
break;
case 2:
str = vrsp_string2;
size = sizeof (vrsp_string2);
break;
case 3:
str = vrsp_string3;
size = sizeof (vrsp_string3);
break;
default:
return -1;
}
return usb_lld_ctrl_send (dev, str, size);
}
return -1;
}
static const uint8_t status_zero;
static void
vrsp_setup_endpoints_for_interface (struct usb_dev *dev,
uint16_t interface, int stop)
{
if (interface == 0)
{
if (!stop)
{
struct vrsp *v = vrsp_get (interface, 0);
usb_lld_setup_endp (dev, ENDP1, EP_BULK, 1, 1);
if (v->screen_mem_owner == 0)
usb_lld_tx_enable_buf (&v->dev, ENDP1, &status_zero, 1);
}
else
{
usb_lld_stall_rx (dev, ENDP1);
usb_lld_stall_tx (dev, ENDP1);
}
}
}
static int
usb_set_configuration (struct usb_dev *dev)
{
uint8_t current_conf;
current_conf = usb_lld_current_configuration (dev);
if (current_conf == 0)
{
if (dev->dev_req.value != 1)
return -1;
usb_lld_set_configuration (dev, 1);
vrsp_setup_endpoints_for_interface (dev, 0, 0);
}
else if (current_conf != dev->dev_req.value)
{
if (dev->dev_req.value != 0)
return -1;
usb_lld_set_configuration (dev, 0);
vrsp_setup_endpoints_for_interface (dev, 0, 1);
}
return usb_lld_ctrl_ack (dev);
}
static int
usb_set_interface (struct usb_dev *dev)
{
uint16_t interface = dev->dev_req.index;
uint16_t alt = dev->dev_req.value;
if (interface >= NUM_INTERFACES)
return -1;
if (alt != 0)
return -1;
else
{
vrsp_setup_endpoints_for_interface (dev, interface, 0);
return usb_lld_ctrl_ack (dev);
}
}
static int
usb_get_interface (struct usb_dev *dev)
{
const uint8_t zero = 0;
uint16_t interface = dev->dev_req.index;
if (interface >= NUM_INTERFACES)
return -1;
/* We don't have alternate interface, so, always return 0. */
return usb_lld_ctrl_send (dev, &zero, 1);
}
static int
usb_get_status_interface (struct usb_dev *dev)
{
const uint16_t status_info = 0;
uint16_t interface = dev->dev_req.index;
if (interface >= NUM_INTERFACES)
return -1;
return usb_lld_ctrl_send (dev, &status_info, 2);
}
static void
vrsp_update_receive (struct vrsp *v, int len)
{
chopstx_mutex_lock (&v->mtx);
if (v->screen_mem_owner == 0 && v->screen_ptr)
{
v->screen_ptr += len / 2;
if (v->screen_ptr < screen_image + 12800)
usb_lld_rx_enable_buf (&v->dev, ENDP1, v->screen_ptr, 64);
}
chopstx_mutex_unlock (&v->mtx);
}
static void
vrsp_update_finish (struct vrsp *v, int value)
{
chopstx_mutex_lock (&v->mtx);
if (v->screen_mem_owner == 0 && v->screen_ptr)
{
v->screen_ptr = NULL;
v->mode = value;
chopstx_cond_signal (&v->cnd);
}
chopstx_mutex_unlock (&v->mtx);
}
static void
usb_tx_done (uint8_t ep_num, uint16_t len)
{
struct vrsp *v = vrsp_get (-1, ep_num);
(void)len;
/*
* Coming here means that notification ("it's ready to accept screen
* data") is retrieved by host. Enable the receive endpoint to
* accept data.
*/
/* EP_NUM should be ENDP1 in this implementation. */
usb_lld_rx_enable_buf (&v->dev, ENDP1, v->screen_ptr, 64);
}
static void
usb_rx_ready (uint8_t ep_num, uint16_t len)
{
struct vrsp *v = vrsp_get (-1, ep_num);
if (ep_num == ENDP1)
vrsp_update_receive (v, len);
}
#define PRIO_VRSP 4
#define STACK_PROCESS_2
#include "stack-def.h"
#define STACK_ADDR_VRSP ((uint32_t)process2_base)
#define STACK_SIZE_VRSP (sizeof process2_base)
static void *
vrsp_main (void *arg)
{
struct vrsp *v = arg;
struct usb_dev *dev = &v->dev;
int e;
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
usb_lld_init (dev, FEATURE_BUS_POWERED);
while (1)
{
chopstx_intr_wait (&usb_intr);
if (usb_intr.ready)
{
uint8_t ep_num;
/*
* When interrupt is detected, call usb_lld_event_handler.
* The event may be one of following:
* (1) Transfer to endpoint (bulk or interrupt)
* In this case EP_NUM is encoded in the variable E.
* (2) "NONE" event: some trasfer was done, but all was
* done by lower layer, no other work is needed in
* upper layer.
* (3) Device events: Reset or Suspend
* (4) Device requests to the endpoint zero.
*
*/
e = usb_lld_event_handler (dev);
chopstx_intr_done (&usb_intr);
ep_num = USB_EVENT_ENDP (e);
if (ep_num != 0)
{
if (USB_EVENT_TXRX (e))
usb_tx_done (ep_num, USB_EVENT_LEN (e));
else
usb_rx_ready (ep_num, USB_EVENT_LEN (e));
}
else
switch (USB_EVENT_ID (e))
{
case USB_EVENT_DEVICE_RESET:
usb_device_reset (dev);
continue;
case USB_EVENT_DEVICE_ADDRESSED:
/* The addres is assigned to the device. We don't
* need to do anything for this actually, but in this
* application, we maintain the USB status of the
* device. Usually, just "continue" as EVENT_OK is
* OK.
*/
chopstx_mutex_lock (&v->mtx);
v->device_state = USB_DEVICE_STATE_ADDRESSED;
chopstx_cond_signal (&v->cnd);
chopstx_mutex_unlock (&v->mtx);
continue;
case USB_EVENT_GET_DESCRIPTOR:
if (usb_get_descriptor (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_SET_CONFIGURATION:
if (usb_set_configuration (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_SET_INTERFACE:
if (usb_set_interface (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_CTRL_REQUEST:
/* Device specific device request. */
if (usb_setup (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_GET_STATUS_INTERFACE:
if (usb_get_status_interface (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_GET_INTERFACE:
if (usb_get_interface (dev) < 0)
usb_lld_ctrl_error (dev);
continue;
case USB_EVENT_SET_FEATURE_DEVICE:
case USB_EVENT_SET_FEATURE_ENDPOINT:
case USB_EVENT_CLEAR_FEATURE_DEVICE:
case USB_EVENT_CLEAR_FEATURE_ENDPOINT:
usb_lld_ctrl_ack (dev);
continue;
case USB_EVENT_CTRL_WRITE_FINISH:
/* Control WRITE transfer finished. */
usb_ctrl_write_finish (dev);
continue;
case USB_EVENT_OK:
case USB_EVENT_DEVICE_SUSPEND:
default:
continue;
}
}
}
return NULL;
}
static int
vrsp_ready_check (void *arg)
{
struct vrsp *v = arg;
return v->screen_mem_owner == 0 && v->screen_ptr == NULL;
}
/* API exposed to application. */
struct vrsp *
vrsp_open (void)
{
struct vrsp *v = &vrsp0; /* Now, we only have a single VRSP. */
chopstx_mutex_init (&v->mtx);
chopstx_cond_init (&v->cnd);
v->device_state = USB_DEVICE_STATE_UNCONNECTED;
v->screen_mem_owner = 0;
v->screen_ptr = screen_image;
v->mode = 1;
chopstx_create (PRIO_VRSP, STACK_ADDR_VRSP, STACK_SIZE_VRSP, vrsp_main, v);
return v;
}
void
vrsp_prepare_poll (struct vrsp *v, chopstx_poll_cond_t *poll_desc)
{
poll_desc->type = CHOPSTX_POLL_COND;
poll_desc->ready = 0;
poll_desc->cond = &v->cnd;
poll_desc->mutex = &v->mtx;
poll_desc->check = vrsp_ready_check;
poll_desc->arg = v;
}
/*
* vrsp_screen_acquire: Acquire the screen memory by the application to
* update the LCD.
*
* Returns < 0 on error
* >= 0 on success
*/
int
vrsp_screen_acquire (struct vrsp *v)
{
int r = 0;
chopstx_mutex_lock (&v->mtx);
while (1)
if (v->screen_mem_owner != 0)
{
/* something goes wrong, it's your turn already. */
r = -1;
break;
}
else if (v->screen_ptr)
/* The use of screen has not been finished yet. */
chopstx_cond_wait (&v->cnd, &v->mtx);
else
{
v->screen_mem_owner = 1;
r = v->mode;
break;
}
chopstx_mutex_unlock (&v->mtx);
return r;
}
/*
* vrsp_screen_release: release the screen memory by the application
*/
void
vrsp_screen_release (struct vrsp *v)
{
chopstx_mutex_lock (&v->mtx);
v->screen_mem_owner = 0;
v->screen_ptr = screen_image;
chopstx_mutex_unlock (&v->mtx);
/* Put the notification to host. */
usb_lld_tx_enable_buf (&v->dev, ENDP1, &status_zero, 1);
}

7
example-lcd/vrsp.h Normal file
View File

@@ -0,0 +1,7 @@
struct vrsp;
struct vrsp *vrsp_open (void);
void vrsp_prepare_poll (struct vrsp *v, chopstx_poll_cond_t *poll_desc);
int vrsp_screen_acquire (struct vrsp *v);
void vrsp_screen_release (struct vrsp *v);

View File

@@ -2,15 +2,21 @@
PROJECT = sample
### Currently, it's for STM32F0 Discovery.
### Currently, it is for STM32 Nucleo L432
###
### Please change lines started with '###' for Cortex-M3 board.
###
### Please change lines started with '###' for Cortex-M0+ board
### (STM32F0 Discovery).
CHOPSTX = ..
LDSCRIPT= sample.ld
LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld
### LDSCRIPT= sample.ld.m3
CSRC = sample.c
CHIP=stm32f0
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes
###################################
@@ -20,9 +26,10 @@ LD = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
### MCU = cortex-m3
MCU = cortex-m0
### MCU = cortex-m0
MCU = cortex-m4
CWARN = -Wall -Wextra -Wstrict-prototypes
DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=48
DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=80
### DEFS = -DFREE_STANDING -DUSE_SYS3 -DBUSY_LOOP -DCHX_FLAGS_MAIN=CHOPSTX_SCHED_RR
OPT = -O3 -Os -g
LIBS =

View File

@@ -1 +1 @@
../board/board-stm32f0-discovery.h
../board/board-st-nucleo-l432.h

View File

@@ -1,6 +1,8 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include "board.h"
#include "sys.h" /* for set_led */
static chopstx_mutex_t mtx;

107
example-led/sample.ld.m4 Normal file
View File

@@ -0,0 +1,107 @@
/*
* ST32L4 memory setup.
*/
MEMORY
{
flash : org = 0x08000000, len = 256k
ram : org = 0x20000000, len = 48k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 20k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
_text = .;
.startup : ALIGN(128) SUBALIGN(128)
{
KEEP(*(.startup.vectors))
. = ALIGN(16);
_sys = .;
. = ALIGN(16);
KEEP(*(.sys.version))
KEEP(*(.sys.board_id))
KEEP(*(.sys.board_name))
build/sys-*.o(.text)
build/sys-*.o(.text.*)
build/sys-*.o(.rodata)
build/sys-*.o(.rodata.*)
. = ALIGN(1024);
} > flash =0xffffffff
.text : ALIGN(16) SUBALIGN(16)
{
*(.text.startup.*)
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
. = ALIGN(8);
} > flash
.ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash
.ARM.exidx : {
PROVIDE(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE(__exidx_end = .);
} > flash
.eh_frame_hdr : {*(.eh_frame_hdr)} > flash
.eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash
.textalign : ONLY_IF_RO { . = ALIGN(8); } > flash
_etext = .;
_textdata = _etext;
.stacks (NOLOAD) :
{
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.data)
. = ALIGN(4);
*(.data.*)
. = ALIGN(4);
*(.ramtext)
. = ALIGN(4);
PROVIDE(_edata = .);
} > ram AT > flash
.bss :
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
} > ram
PROVIDE(end = .);
_end = .;
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

View File

@@ -8,6 +8,7 @@ LDSCRIPT= lcd.ld
CSRC = primer2-switches.c primer2-ts.c lcd.c main.c \
neug.c sha256.c
ARCH=cortex-m
CHIP=stm32f103
USE_SYS = yes
USE_USB = yes

48
example-usart/Makefile Normal file
View File

@@ -0,0 +1,48 @@
# Makefile for example application of Chopstx
PROJECT = sample
### Currently, it is for STM32 Nucleo L432
###
### Please change lines started with '###' for Cortex-M3 board.
###
### Please change lines started with '###' for Cortex-M0+ board
### (STM32F0 Discovery).
CHOPSTX = ..
LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld
### LDSCRIPT= sample.ld.m3
CSRC = sample.c
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes
USE_USART = yes
###################################
CROSS = arm-none-eabi-
CC = $(CROSS)gcc
LD = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
### MCU = cortex-m3
### MCU = cortex-m0
MCU = cortex-m4
CWARN = -Wall -Wextra -Wstrict-prototypes
DEFS = -DUSE_SYS3 -DFREE_STANDING -DMHZ=80
### DEFS = -DFREE_STANDING -DUSE_SYS3 -DBUSY_LOOP -DCHX_FLAGS_MAIN=CHOPSTX_SCHED_RR
OPT = -O3 -Os -g
LIBS =
####################
include ../rules.mk
board.h:
@echo Please make a symbolic link \'board.h\' to a file in ../board;
@exit 1
sys.c: board.h
distclean: clean
rm -f board.h

1
example-usart/board.h Symbolic link
View File

@@ -0,0 +1 @@
../board/board-st-nucleo-l432.h

156
example-usart/sample.c Normal file
View File

@@ -0,0 +1,156 @@
#include <stdint.h>
#include <stdlib.h>
#include <chopstx.h>
#include "board.h"
#include "sys.h" /* for set_led */
#include <contrib/usart.h>
static chopstx_mutex_t mtx;
static chopstx_cond_t cnd0;
static chopstx_cond_t cnd1;
static uint8_t u, v;
static uint8_t m; /* 0..100 */
static void
wait_for (uint32_t usec)
{
#if defined(BUSY_LOOP)
uint32_t count = usec * 6;
uint32_t i;
for (i = 0; i < count; i++)
asm volatile ("" : : "r" (i) : "memory");
#else
chopstx_usec_wait (usec);
#endif
}
static void *
pwm (void *arg)
{
(void)arg;
chopstx_mutex_lock (&mtx);
chopstx_cond_wait (&cnd0, &mtx);
chopstx_mutex_unlock (&mtx);
while (1)
{
set_led (u&v);
wait_for (m);
set_led (0);
wait_for (100-m);
}
return NULL;
}
static void *
blk (void *arg)
{
(void)arg;
chopstx_mutex_lock (&mtx);
chopstx_cond_wait (&cnd1, &mtx);
chopstx_mutex_unlock (&mtx);
while (1)
{
v = 0;
wait_for (200*1000);
v = 1;
wait_for (200*1000);
}
return NULL;
}
#if defined(BUSY_LOOP)
#define PRIO_PWM (CHOPSTX_SCHED_RR|1)
#define PRIO_BLK (CHOPSTX_SCHED_RR|1)
#else
#define PRIO_PWM 3
#define PRIO_BLK 2
#endif
#define STACK_MAIN
#define STACK_PROCESS_1
#define STACK_PROCESS_2
#define STACK_PROCESS_3
#include "stack-def.h"
#define STACK_ADDR_PWM ((uint32_t)process1_base)
#define STACK_SIZE_PWM (sizeof process1_base)
#define STACK_ADDR_BLK ((uint32_t)process2_base)
#define STACK_SIZE_BLK (sizeof process2_base)
#define PRIO_USART 4
#define STACK_ADDR_USART ((uint32_t)process3_base)
#define STACK_SIZE_USART (sizeof process3_base)
static int
ss_notify (uint8_t dev_no, uint16_t state_bits)
{
(void)dev_no;
(void)state_bits;
return 0;
}
int
main (int argc, const char *argv[])
{
chopstx_poll_cond_t poll_desc;
uint32_t timeout;
struct chx_poll_head *ph[1];
(void)argc;
(void)argv;
chopstx_mutex_init (&mtx);
chopstx_cond_init (&cnd0);
chopstx_cond_init (&cnd1);
m = 10;
chopstx_create (PRIO_PWM, STACK_ADDR_PWM, STACK_SIZE_PWM, pwm, NULL);
chopstx_create (PRIO_BLK, STACK_ADDR_BLK, STACK_SIZE_BLK, blk, NULL);
chopstx_usec_wait (200*1000);
chopstx_mutex_lock (&mtx);
chopstx_cond_signal (&cnd0);
chopstx_cond_signal (&cnd1);
chopstx_mutex_unlock (&mtx);
usart_init (PRIO_USART, STACK_ADDR_USART, STACK_SIZE_USART, ss_notify);
usart_config (2, B115200 | CS8 | STOP1B);
usart_read_prepare_poll (2, &poll_desc);
ph[0] = (struct chx_poll_head *)&poll_desc;
timeout = 200*1000*6;
while (1)
{
chopstx_poll (&timeout, 1, ph);
if (timeout == 0)
{
usart_write (2, "Hello\r\n", 7);
u ^= 1;
timeout = 200*1000*6;
}
else
{
char buf[256];
int r;
r = usart_read (2, buf, 256);
if (r)
usart_write (2, buf, r);
}
}
return 0;
}

126
example-usart/sample.ld Normal file
View File

@@ -0,0 +1,126 @@
/*
* ST32F0 memory setup.
*/
MEMORY
{
flash0 : org = 0x08000000, len = 4k
flash : org = 0x08000000+0x1000, len = 60k
ram : org = 0x20000000, len = 20k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 20k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
.sys : ALIGN(4) SUBALIGN(4)
{
_sys = .;
KEEP(*(.vectors))
. = ALIGN(16);
KEEP(*(.sys.version))
KEEP(*(.sys.board_id))
KEEP(*(.sys.board_name))
build/sys-*.o(.text)
build/sys-*.o(.text.*)
build/sys-*.o(.rodata)
build/sys-*.o(.rodata.*)
. = ALIGN(1024);
/*
*(.sys.0)
*(.sys.1)
*(.sys.2)
*/
} > flash0 =0xffffffff
_text = .;
.startup : ALIGN(128) SUBALIGN(128)
{
KEEP(*(.startup.vectors))
. = ALIGN (16);
} > flash =0xffffffff
.text : ALIGN(16) SUBALIGN(16)
{
*(.text.startup.*)
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
. = ALIGN(8);
} > flash
.ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash
.ARM.exidx : {
PROVIDE(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE(__exidx_end = .);
} > flash
.eh_frame_hdr : {*(.eh_frame_hdr)} > flash
.eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash
.textalign : ONLY_IF_RO { . = ALIGN(8); } > flash
_etext = .;
_textdata = _etext;
.vectors_in_ram :
{
. = ALIGN(8);
__vector_ram_addr__ = .;
KEEP(*(.bss.startup.*))
} > ram
.stacks (NOLOAD) :
{
. = ALIGN(8);
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.data)
. = ALIGN(4);
*(.data.*)
. = ALIGN(4);
*(.ramtext)
. = ALIGN(4);
PROVIDE(_edata = .);
} > ram AT > flash
.bss :
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
} > ram
PROVIDE(end = .);
_end = .;
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

116
example-usart/sample.ld.m3 Normal file
View File

@@ -0,0 +1,116 @@
/*
* ST32F103 memory setup.
*/
MEMORY
{
flash0 : org = 0x08000000, len = 4k
flash : org = 0x08000000+0x1000, len = 60k
ram : org = 0x20000000, len = 20k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 20k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
.sys : ALIGN(4) SUBALIGN(4)
{
_sys = .;
KEEP(*(.vectors))
. = ALIGN(16);
KEEP(*(.sys.version))
KEEP(*(.sys.board_id))
KEEP(*(.sys.board_name))
build/sys-*.o(.text)
build/sys-*.o(.text.*)
build/sys-*.o(.rodata)
build/sys-*.o(.rodata.*)
. = ALIGN(1024);
*(.sys.0)
*(.sys.1)
*(.sys.2)
} > flash0
_text = .;
.startup : ALIGN(128) SUBALIGN(128)
{
KEEP(*(.startup.vectors))
. = ALIGN (16);
} > flash =0xffffffff
.text : ALIGN(16) SUBALIGN(16)
{
*(.text.startup.*)
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
. = ALIGN(8);
} > flash
.ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash
.ARM.exidx : {
PROVIDE(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE(__exidx_end = .);
} > flash
.eh_frame_hdr : {*(.eh_frame_hdr)} > flash
.eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash
.textalign : ONLY_IF_RO { . = ALIGN(8); } > flash
_etext = .;
_textdata = _etext;
.stacks (NOLOAD) :
{
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.data)
. = ALIGN(4);
*(.data.*)
. = ALIGN(4);
*(.ramtext)
. = ALIGN(4);
PROVIDE(_edata = .);
} > ram AT > flash
.bss :
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
} > ram
PROVIDE(end = .);
_end = .;
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

107
example-usart/sample.ld.m4 Normal file
View File

@@ -0,0 +1,107 @@
/*
* ST32L4 memory setup.
*/
MEMORY
{
flash : org = 0x08000000, len = 256k
ram : org = 0x20000000, len = 48k
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = 20k;
__ram_end__ = __ram_start__ + __ram_size__;
SECTIONS
{
. = 0;
_text = .;
.startup : ALIGN(128) SUBALIGN(128)
{
KEEP(*(.startup.vectors))
. = ALIGN(16);
_sys = .;
. = ALIGN(16);
KEEP(*(.sys.version))
KEEP(*(.sys.board_id))
KEEP(*(.sys.board_name))
build/sys-*.o(.text)
build/sys-*.o(.text.*)
build/sys-*.o(.rodata)
build/sys-*.o(.rodata.*)
. = ALIGN(1024);
} > flash =0xffffffff
.text : ALIGN(16) SUBALIGN(16)
{
*(.text.startup.*)
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
. = ALIGN(8);
} > flash
.ARM.extab : {*(.ARM.extab* .gnu.linkonce.armextab.*)} > flash
.ARM.exidx : {
PROVIDE(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE(__exidx_end = .);
} > flash
.eh_frame_hdr : {*(.eh_frame_hdr)} > flash
.eh_frame : ONLY_IF_RO {*(.eh_frame)} > flash
.textalign : ONLY_IF_RO { . = ALIGN(8); } > flash
_etext = .;
_textdata = _etext;
.stacks (NOLOAD) :
{
*(.main_stack)
*(.process_stack.0)
*(.process_stack.1)
*(.process_stack.2)
*(.process_stack.3)
. = ALIGN(8);
} > ram
.data :
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.data)
. = ALIGN(4);
*(.data.*)
. = ALIGN(4);
*(.ramtext)
. = ALIGN(4);
PROVIDE(_edata = .);
} > ram AT > flash
.bss :
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
*(.bss)
. = ALIGN(4);
*(.bss.*)
. = ALIGN(4);
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
} > ram
PROVIDE(end = .);
_end = .;
}
__heap_base__ = _end;
__heap_end__ = __ram_end__;

54
example-usart/stack-def.h Normal file
View File

@@ -0,0 +1,54 @@
#define MAIN_SIZE 0x0080 /* Idle+Exception handlers */
#define SIZE_0 0x0300 /* Main program */
#define SIZE_1 0x0100 /* first thread program */
#define SIZE_2 0x0100 /* second thread program */
#define SIZE_3 0x0200 /* third thread program */
#if defined(STACK_MAIN)
/*
* The terminology of "main" is confusing in ARM architecture.
* Here, "main_base" is for exception handlers.
*/
/* Idle+Exception handlers */
char __main_stack_end__[0] __attribute__ ((section(".main_stack")));
char main_base[MAIN_SIZE] __attribute__ ((section(".main_stack")));
/* Main program */
char __process0_stack_end__[0] __attribute__ ((section(".process_stack.0")));
char process0_base[SIZE_0] __attribute__ ((section(".process_stack.0")));
#endif
/* First thread program */
#if defined(STACK_PROCESS_1)
char process1_base[SIZE_1] __attribute__ ((section(".process_stack.1")));
#endif
/* Second thread program */
#if defined(STACK_PROCESS_2)
char process2_base[SIZE_2] __attribute__ ((section(".process_stack.2")));
#endif
/* Third thread program */
#if defined(STACK_PROCESS_3)
char process3_base[SIZE_3] __attribute__ ((section(".process_stack.3")));
#endif
/* Fourth thread program */
#if defined(STACK_PROCESS_4)
char process4_base[SIZE_4] __attribute__ ((section(".process_stack.4")));
#endif
/* Fifth thread program */
#if defined(STACK_PROCESS_5)
char process5_base[SIZE_5] __attribute__ ((section(".process_stack.5")));
#endif
/* Sixth thread program */
#if defined(STACK_PROCESS_6)
char process6_base[SIZE_6] __attribute__ ((section(".process_stack.6")));
#endif
/* Seventh thread program */
#if defined(STACK_PROCESS_7)
char process7_base[SIZE_7] __attribute__ ((section(".process_stack.7")));
#endif

View File

@@ -6,6 +6,7 @@ CHOPSTX = ..
LDSCRIPT= sample.ld
CSRC = sample.c usb-cdc.c
ARCH=cortex-m
CHIP=stm32f103
USE_SYS = yes

View File

@@ -722,31 +722,8 @@ cdc_main (void *arg)
(void)arg;
#if defined(OLDER_SYS_H)
/*
* Historically (before sys < 3.0), NVIC priority setting for USB
* interrupt was done in usb_lld_sys_init. Thus this code.
*
* When USB interrupt occurs between usb_lld_init (which assumes
* ISR) and chopstx_claim_irq (which clears pending interrupt),
* invocation of usb_lld_event_handler won't occur.
*
* Calling usb_lld_event_handler is no harm even if there were no
* interrupts, thus, we call it unconditionally here, just in case
* if there is a request.
*
* We can't call usb_lld_init after chopstx_claim_irq, as
* usb_lld_init does its own setting for NVIC. Calling
* chopstx_claim_irq after usb_lld_init overrides that.
*
*/
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
goto event_handle;
#else
chopstx_claim_irq (&usb_intr, INTR_REQ_USB);
usb_lld_init (&dev, VCOM_FEATURE_BUS_POWERED);
#endif
while (1)
{
@@ -754,9 +731,7 @@ cdc_main (void *arg)
if (usb_intr.ready)
{
uint8_t ep_num;
#if defined(OLDER_SYS_H)
event_handle:
#endif
/*
* When interrupt is detected, call usb_lld_event_handler.
* The event may be one of following:

View File

@@ -1,8 +1,7 @@
Consideration about SYS and the first pages of flash ROM
========================================================
Now, I'm developing something like SYS for Kinetis L MCU, so, I write
this document.
This document was written when I was porting Chopstx to Kinetis L.
* Compatibility
@@ -10,7 +9,8 @@ this document.
SYS 2.0: Added clock_init, gpio_init
SYS 2.1: Added sys_board_id, sys_board_name
SYS 3.0: Don't setup NVIC priority by usb_lld_sys_init
SYS 4.0: For USB, only do usb_cable_config. Enabling/disabling the
USB module is the role of USB driver.
* Macro definition by DEFS in Makefile
@@ -131,23 +131,24 @@ and here is the list of all.
nvic_system_reset
The routines of clock_init and gpio_init are here because of some
historical reasons. (We could design a system with no such exported
routines: by defining: those things done internally after reset and
before calling the application.)
The routines of clock_init and gpio_init exist in SYS, because of some
historical reasons; Without such exported routines, we could design a
system having an assumption: important clock and gpio initialization
is done internally after reset, so that an application can just work.
Those are exported as entries of SYS, and it is the responsibility of
the application which do initialize clock and GPIO, calling those
routines.
USB routines are needed because of hardware practice of STM32F103.
With STM32F103, each board has different way for handling the pull up
of USB D+ and how the device asks re-enumeration to host PC. In my
opinion, if it's defined as full speed device and it's OK for us not
to use high impedance (but asserting to LOW, instead) of D+ to ask
re-enumeration, we can just pull up D+ always. And we wouldn't need
such routines in SYS.
USB routines are needed because of hardware practice of STM32F103. (It
can support low speed device. It can support self powered USB
system.) With STM32F103 (for allowing low speed device and self
powered system), each board has different way for handling the pull up
of USB D+ and how the device asks re-enumeration to host PC.
For bus-powered system and full speed device, we can just pull up D+
always. Still, support of high impedance state of D+ line would be
ideal when asking re-enumeration, asserting to LOW works.
About SYS on Kinetis L

32
mcu/ABOUT-USB Normal file
View File

@@ -0,0 +1,32 @@
USB driver in Chopstx
Full speed device is assumed. Bus powered system is assumed.
API-wise, self powered system is not supported (yet) by this USB driver.
The driver can be used without Chopstx. An example can be find in
Gnuk (gnuk/regnual).
The USB driver was originally written for STM32F103, which USB
hardware design is considered not mature. Modern USB hardware design
allows crystal-less design, and/or comes with internal 5V->3V3
regulator, D+-line pull-up support, and VBUS detection. STM32F103 has
nothing.
To support self powered system, we need to define a hardware interface
for a board detecting VBUS voltage. Only after detecting VBUS power,
we can enable USB driver (D+/D- lines). For self powered system,
driving D+/D- lines by fixed pull-up resistor violates the USB
specification.
With STM32F103, there is a common hardware practice having a
gate/transistor for pull-up D+ line. If it also supports detecting
VBUS, self powerd system can be supported with the gate/transistor.
Such a gate/transistor can be also used for the board to ask
re-enumeration of USB device. Asking re-enumeration, it is enough to
have SE0 (Single-Ended Zero) state.
The USB driver doesn't touch such a gate/transistor. By gpio_init,
the D+-line should be asserted, USB D+ and D- lines should be drived
to 0 (SE0). When the USB module is enabled, it goes into J and K
state (from SE0 state).

22
mcu/chx-gd32vf103.c Normal file
View File

@@ -0,0 +1,22 @@
#include <stdint.h>
#include <mcu/gd32vf103.h>
asm (
".equ wfe,0x810\n\t" /* Not used (yet). */
".equ sleepvalue,0x811" /* Not used (yet). */
);
extern int chx_allow_sleep;
void
chx_sleep_mode (int how)
{
/*TBD*/
(void)how;
}
void
chx_prepare_sleep_mode (void)
{
/*TBD*/
}

29
mcu/chx-stm32l4.c Normal file
View File

@@ -0,0 +1,29 @@
#include <stdint.h>
#include <mcu/cortex-m.h>
#include <mcu/stm32l.h>
extern int chx_allow_sleep;
void
chx_sleep_mode (int how)
{
/*TBD*/
(void)how;
}
void __attribute__((naked))
chx_idle (void)
{
/*TBD*/
int sleep_enabled;
for (;;)
{
asm ("ldr %0, %1" : "=r" (sleep_enabled): "m" (chx_allow_sleep));
if (sleep_enabled)
{
asm volatile ("wfi" : : : "memory");
/* NOTE: it never comes here. Don't add lines after this. */
}
}
}

View File

@@ -0,0 +1,79 @@
#include <stdint.h>
#include <mcu/gd32vf103.h>
static void __attribute__((used,section(".text.startup.1")))
clock_init (void)
{
/* HXTAL setup */
RCU->CTL |= RCU_CTL_HXTALEN;
while (!(RCU->CTL & RCU_CTL_HXTALSTB))
;
RCU->CFG0 &= ~RCU_CFG0_AHB_APB1_APB2_MASK;
/* CK_AHB = CK_SYS */
RCU->CFG0 |= RCU_CFG0_AHB_CKSYS_DIV1;
/* CK_APB2 = CK_AHB */
RCU->CFG0 |= RCU_CFG0_APB2_CKAHB_DIV1;
/* CK_APB1 = CK_AHB/2 */
RCU->CFG0 |= RCU_CFG0_APB1_CKAHB_DIV2;
/* CK_USBFS = 48MHz */
RCU->CFG0 |= RCU_CFG0_USBFSPSC_DIV2;
/* CK_ADC, CK_TIMER1xxx, CK_TIMER0, CK_I2S */
/* PLL setup */
RCU->CFG0 &= ~RCU_CFG0_PLLSRC_PLLMF_MASK;
RCU->CFG0 |= RCU_CFG0_PLL_MUL_VALUE | RCU_CFG0_PLLSRC_HXTAL;
RCU->CFG1 &= ~RCU_CFG1_PREDV0SEL_MASK;
RCU->CFG1 |= RCU_CFG1_PREDV0SEL_HXTAL;
RCU->CTL |= RCU_CTL_PLLEN;
while (!(RCU->CTL & RCU_CTL_PLLSTB))
;
/* Select PLL as system clock */
RCU->CFG0 &= ~RCU_CFG0_SCS_MASK;
RCU->CFG0 |= RCU_CFG0_CKSYSSRC_PLL;
/* Wait until PLL is selected as system clock */
while (!(RCU->CFG0 & RCU_CFG0_SCSS_PLL))
;
/* Stop IRC8M */
RCU->CTL &= ~RCU_CTL_IRC8MEN;
/* Flash setup: TBD */
}
static void __attribute__((used,section(".text.startup.1")))
gpio_init (void)
{
RCU->APB2EN |= RCU_APB2_GPIO;
RCU->APB2RST = RCU_APB2_GPIO;
RCU->APB2RST = 0;
#ifdef AFIO_MAPR_SOMETHING
AFIO->MAPR |= AFIO_MAPR_SOMETHING;
#endif
/* LED is mandatory. We configure it always. */
GPIO_LED->ODR = VAL_GPIO_LED_ODR;
GPIO_LED->CRH = VAL_GPIO_LED_CRH;
GPIO_LED->CRL = VAL_GPIO_LED_CRL;
/* If there is USB enabler pin and it's independent, we configure it. */
#if defined(GPIO_USB) && defined(VAL_GPIO_USB_ODR)
GPIO_USB->ODR = VAL_GPIO_USB_ODR;
GPIO_USB->CRH = VAL_GPIO_USB_CRH;
GPIO_USB->CRL = VAL_GPIO_USB_CRL;
#endif
#ifdef GPIO_OTHER
GPIO_OTHER->ODR = VAL_GPIO_OTHER_ODR;
GPIO_OTHER->CRH = VAL_GPIO_OTHER_CRH;
GPIO_OTHER->CRL = VAL_GPIO_OTHER_CRL;
#endif
}

View File

@@ -22,7 +22,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/

View File

@@ -22,7 +22,7 @@
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* receipents of GNU GPL by a written offer.
* recipients of GNU GPL by a written offer.
*
*/

115
mcu/clk_gpio_init-stm32l.c Normal file
View File

@@ -0,0 +1,115 @@
/*
* clk_gpio_init-stm32l.c - Clock and GPIO initialization for STM32L.
*
* Copyright (C) 2019 Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
* This file is a part of Chopstx, a thread library for embedded.
*
* Chopstx 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 3 of the License, or
* (at your option) any later version.
*
* Chopstx 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, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU GPL version 3 section 7, you may
* distribute non-source form of the Program without the copy of the
* GNU GPL normally required by section 4, provided you inform the
* recipients of GNU GPL by a written offer.
*
*/
#include <mcu/stm32l.h>
#define STM32_FLASHBITS 0x00000704
void
clock_init (void)
{
/* MSI: 4MHz (keep the default value) */
/* PLL input: MSI at 4MHz, VCO: 160 MHz, output 80MHz */
RCC->PLLCFGR = ((1 << 24) | (40 << 8)) | 0x01;
/* Enable PLL */
RCC->CR |= (1 << 24);
while (!(RCC->CR & (1 << 25)))
;
/* Flash setup: four wait states at 80MHz */
FLASH->ACR = STM32_FLASHBITS;
while ((FLASH->ACR & 0x07) != (STM32_FLASHBITS & 0x07))
;
/* Configure bus clocks: AHB: 80MHz, APB1: 40MHz, APB2: 40MHz */
RCC->CFGR = ((0x04 << 11) | (0x04 << 8));
/* Switch SYSCLOCK using PLL */
RCC->CFGR |= 0x03;
while ((RCC->CFGR & 0x0C) != 0x0C)
;
/* Peripheral clock selection */
RCC->CCIPR = ( (0x00 << 26) | /* HSI48 for USB */
(0x00 << 2) | /* PCLK for USART2 */
(0x00 << 0) ); /* PCLK for USART1 */
/* Enable PWR clock */
RCC->APB1ENR1 |= (1 << 28);
RCC->APB1RSTR1 = (1 << 28);
RCC->APB1RSTR1 = 0;
/* Enable HSI48 clock */
RCC->CRRCR |= 1;
while ((RCC->CRRCR & 0x02) == 0)
;
}
static struct GPIO *const GPIO_LED = (struct GPIO *)GPIO_LED_BASE;
#ifdef GPIO_USB_BASE
static struct GPIO *const GPIO_USB = (struct GPIO *)GPIO_USB_BASE;
#endif
#ifdef GPIO_OTHER_BASE
static struct GPIO *const GPIO_OTHER = (struct GPIO *)GPIO_OTHER_BASE;
#endif
void
gpio_init (void)
{
/* Enable GPIO clock. */
RCC->AHB2ENR |= RCC_AHB2_GPIO;
/* Delay (more than two clocks) is needed. */
while ((RCC->AHB2ENR & RCC_AHB2_GPIO) == 0)
;
RCC->AHB2RSTR = RCC_AHB2_GPIO;
RCC->AHB2RSTR = 0;
/* Delay (more than two clocks) is needed. */
while (RCC->AHB2RSTR != 0)
;
/* LED is mandatory. We configure it always. */
GPIO_LED->OSPEEDR = VAL_GPIO_LED_OSPEEDR;
GPIO_LED->OTYPER = VAL_GPIO_LED_OTYPER;
GPIO_LED->MODER = VAL_GPIO_LED_MODER;
GPIO_LED->PUPDR = VAL_GPIO_LED_PUPDR;
GPIO_LED->AFRL = VAL_GPIO_LED_AFRL;
GPIO_LED->AFRH = VAL_GPIO_LED_AFRH;
#ifdef GPIO_OTHER_BASE
GPIO_OTHER->OSPEEDR = VAL_GPIO_OTHER_OSPEEDR;
GPIO_OTHER->OTYPER = VAL_GPIO_OTHER_OTYPER;
GPIO_OTHER->MODER = VAL_GPIO_OTHER_MODER;
GPIO_OTHER->PUPDR = VAL_GPIO_OTHER_PUPDR;
GPIO_OTHER->AFRL = VAL_GPIO_OTHER_AFRL;
GPIO_OTHER->AFRH = VAL_GPIO_OTHER_AFRH;
#endif
}

Some files were not shown because too many files have changed in this diff Show More