68 Commits

Author SHA1 Message Date
NIIBE Yutaka
9977bac715 Version 2.3.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-12 11:09:38 +09:00
NIIBE Yutaka
446e31a7c4 usbip: Fix the value of URB_DATA_SIZE again.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-11 10:22:19 +09:00
NIIBE Yutaka
f10cdce66c more fix for libccid.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-04 13:58:44 +09:00
NIIBE Yutaka
86ccc02be7 Fix USB emulation driver for GNU/Linux.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-04 10:05:45 +09:00
NIIBE Yutaka
d36e9274b1 Add comment for chopstx_mutex_lock.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2021-10-04 09:26:50 +09:00
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
60 changed files with 8826 additions and 1028 deletions

44
AUTHORS
View File

@@ -34,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-stm32f.c,
cortex-m.h, mkl27z.h, stm32.h, stm32f103.h,
sys-gnu-linux.c,sys-gnu-linux.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.

240
ChangeLog
View File

@@ -1,27 +1,128 @@
2021-02-18 NIIBE Yutaka <gniibe@fsij.org>
2021-10-12 NIIBE Yutaka <gniibe@fsij.org>
* VERSION: 1.19.
* doc/chopstx.texi (VERSION): 1.19.
* VERSION: 2.3
* doc/chopstx.texi (VERSION): 2.3.
2021-10-11 NIIBE Yutaka <gniibe@fsij.org>
* mcu/usb-usbip.c (URB_DATA_SIZE): Tweak the value.
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>
Backport from master.
* contrib/ackbtn-gnu-linux.c: New.
* mcu/usb-usbip.c (usbip_run_server): Add start-up message.
* 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>
Backport from 2.1.
* chopstx-gnu-linux.c (chopstx_create_arch): Clear TP.
* 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.
2019-12-30 NIIBE Yutaka <gniibe@fsij.org>
* chopstx-gnu-linux.c (voluntary_context_switch): Fix for the case
when chx_idle suggests no context switch (tp_prev == tp_next).
* VERSION: 1.18.
* doc/chopstx.texi (VERSION): 1.18.
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>
@@ -32,6 +133,123 @@
* 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.

65
NEWS
View File

@@ -1,9 +1,18 @@
NEWS - Noteworthy changes
* Major changes in Chopstx 1.19
* Major changes in Chopstx 2.3
Released 2021-02-18
Released 2021-10-12
** Fix of USB driver for GNU/Linux emulation
Fix the value of URB_DATA_SIZE, so that Gnuk can work with PC/SC.
* 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.
@@ -14,11 +23,55 @@ 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
When creating a thread, allocated memory for struct chx_thread was not
initialized by chopstx_create_arch, assuming getcontext does it well.
To make sure (getcontext would not update the area for FPU registers,
when not used), it's now initialized by zeros.
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

19
README
View File

@@ -1,6 +1,6 @@
Chopstx - Threads and only Threads
Version 1.19
2021-02-18
Version 2.3
2021-10-12
Niibe Yutaka
Flying Stone Technology
@@ -9,7 +9,8 @@ What's Chopstx?
Chopstx is an RT thread library for STM32F103 and GD32F103 (ARM
Cortex-M3), STM32F030 (ARM Cortex-M0), MKL27Z (ARM Cortex-M0plus),
STM32L432 (ARM Cortex-M4), and emulation on GNU/Linux.
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.
@@ -27,7 +28,8 @@ Note that this library is _not_ related to the hand game:
https://en.wikipedia.org/wiki/Chopsticks_(hand_game)
Thanks to Yao Wei and Enrico Zini for giving me an opportunity
visiting the wiki page above, when my children were playing the game.
visiting the wiki page above in Debconf Taiwan, when my children were
playing the game.
License
@@ -60,14 +62,11 @@ For STM32 Primer2, see the directory: example-primer2.
Future Works
============
RISC-V port (for GD32VF103) is available in 2.x. Please have a look
at the master branch, and test it if possible.
Convenience function to determine the bottom of thread stack,
configuration of thread size by compiler's output would be next things
to be done.
Experimental SMP port for Cortex-A7 is under development. For SMP,
more careful considerations for shared access to objects of struct
chx_pq is needed. So, modifications required will not be small.
We have an experimental SMP port for Cortex-A7. For SMP, more careful
considerations for shared access to objects of struct chx_pq is
needed. So, modifications required will not be small.
--

View File

@@ -1 +1 @@
release/1.19
release/2.3

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 */

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

@@ -2,7 +2,7 @@
* chopstx-cortex-m.c - Threads and only threads: Arch specific code
* for Cortex-M0/M3/M4
*
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2021
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -77,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
@@ -85,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)
* =====================================
*/
@@ -95,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__) || 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
@@ -230,63 +226,134 @@ 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");
asm volatile ("cpsid i" : : : "memory");
#else
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_INHIBIT_SCHED;
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");
asm volatile ("cpsie i" : : : "memory");
#else
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
register uint32_t tmp = CPU_EXCEPTION_PRIORITY_CLEAR;
asm volatile ("msr BASEPRI, %0" : : "r" (tmp) : "memory");
#endif
}
}
void
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 */
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
@@ -296,51 +363,26 @@ chx_init_arch (struct chx_thread *tp)
chx_set_running (tp);
}
static void
chx_request_preemption (uint16_t prio)
{
if (running == NULL || (uint16_t)running->prio < prio)
{
*ICSR = (1 << 28);
asm volatile ("" : : : "memory");
}
}
/*
* 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__) || 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
@@ -349,45 +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 ();
: "cc", "r2", "r3", "r4", "r5", "r6", "r7", "memory");
asm volatile (/* Now, r0 points to the thread to be switched. */
/* Put it to *running. */
@@ -408,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"
@@ -417,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
@@ -439,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"
/**/
@@ -457,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"
/**/
@@ -469,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);
@@ -514,170 +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);
}
}
/* Registers on stack (PSP): r0, r1, r2, r3, r12, lr, pc, xpsr */
tp = chx_ready_pop ();
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__) || 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"
@@ -686,23 +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);
}
tp = chx_ready_pop ();
: "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

@@ -27,6 +27,16 @@
*
*/
/*
* 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.
*
*/
#include <unistd.h>
#include <ucontext.h>
#include <signal.h>
@@ -54,6 +64,8 @@ chx_dmb (void)
}
static void preempted_context_switch (struct chx_thread *tp_next);
static sigset_t ss_cur;
static void
@@ -144,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;
tp_next = chx_recv_irq (irq_num);
if (!tp_next)
return;
ll_dequeue (p);
chx_wakeup (p);
chx_spin_unlock (&q_intr.lock);
chx_request_preemption (px->master->prio);
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
@@ -193,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);
}
@@ -213,112 +243,56 @@ 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)
{
ucontext_t *tcp;
struct chx_thread *tp_prev;
struct chx_thread *tp = chx_running ();
struct chx_thread *tp_prev = chx_running ();
if (tp && (uint16_t)tp->prio >= prio)
return;
/* Change the context to another thread with higher priority. */
tp_prev = tp;
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);
}
tp = chx_ready_pop ();
if (tp)
tcp = &tp->tc;
else
tcp = &idle_tc;
chx_set_running (tp);
if (tp_prev)
{
/*
* The swapcontext implementation may reset sigmask in the
* middle of its execution, unfortunately. It is best if
* sigmask restore is done at the end of the routine, but we
* can't assume that.
*
* Thus, there might be a race condition with regards to the
* user context TCP, if signal mask is cleared and signal comes
* in. To avoid this situation, we block signals.
*
* We don't need to fill the mask here. It keeps the condition
* of blocking signals before&after swapcontext call. It is
* done by the signal mask for sigaction, the initial creation
* 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);
}
/*
* The swapcontext implementation may reset sigmask in the
* middle of its execution, unfortunately. It is best if
* sigmask restore is done at the end of the routine, but we
* can't assume that.
*
* Thus, there might be a race condition with regards to the
* user context TCP, if signal mask is cleared and signal comes
* in. To avoid this situation, we block signals.
*
* We don't need to fill the mask here. It keeps the condition
* of blocking signals before&after swapcontext call. It is
* done by the signal mask for sigaction, the initial creation
* of the thread, and the condition of chx_sched function which
* mandates holding cpu_sched_lock.
*/
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;
ucontext_t *tcp;
struct chx_thread *tp;
struct chx_thread *tp_prev;
tp = tp_prev = chx_running ();
if (yield)
tp_prev = chx_running ();
if (!tp_next)
{
if (tp->flag_sched_rr)
chx_timer_dequeue (tp);
chx_ready_enqueue (tp);
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);
}
tp = chx_ready_pop ();
if (tp)
tcp = &tp->tc;
else
tcp = &idle_tc;
chx_set_running (tp);
swapcontext (&tp_prev->tc, tcp);
{
chx_set_running (tp_next);
swapcontext (&tp_prev->tc, &tp_next->tc);
}
chx_cpu_sched_unlock ();
tp = chx_running ();
@@ -355,8 +329,16 @@ chopstx_create_arch (uintptr_t stack_addr, size_t stack_size,
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

166
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>
*
@@ -73,11 +73,7 @@ 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;
@@ -101,9 +97,8 @@ 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 struct chx_thread *chx_timer_insert (struct chx_thread *tp, uint32_t usec);
static uint32_t chx_timer_dequeue (struct chx_thread *tp);
@@ -297,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)
@@ -384,7 +379,7 @@ chx_timer_dequeue (struct chx_thread *tp)
}
void
static struct chx_thread *
chx_timer_expired (void)
{
struct chx_thread *tp;
@@ -392,7 +387,9 @@ chx_timer_expired (void)
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;
@@ -404,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;
@@ -424,13 +423,115 @@ 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);
}
@@ -482,9 +583,6 @@ chx_init (struct chx_thread *tp)
tp->parent = NULL;
tp->v = 0;
if (CHX_PRIO_MAIN_INIT >= CHOPSTX_PRIO_INHIBIT_PREEMPTION)
chx_cpu_sched_lock ();
tp->prio = CHX_PRIO_MAIN_INIT;
chopstx_main = (chopstx_t)tp;
@@ -851,7 +949,9 @@ chopstx_mutex_lock (chopstx_mutex_t *mutex)
* chopstx_mutex_unlock - Unlock the mutex
* @mutex: Mutex
*
* Unlock @mutex.
* Unlock @mutex. Note that Chopstx doesn't allow unlocking by
* non-owner of the lock. chopstx_mutex_unlock should be called
* by a thread which did chopstx_mutex_lock.
*/
void
chopstx_mutex_unlock (chopstx_mutex_t *mutex)
@@ -1562,7 +1662,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;
@@ -1602,3 +1702,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.
@@ -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 {
@@ -163,3 +162,5 @@ int chopstx_poll (uint32_t *usec_p, int n,
struct chx_poll_head *const pd_array[]);
int chopstx_conf_idle (int enable_sleep);
void *chopstx_critical (void *func (void *), void *arg);

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;
}

View File

@@ -203,7 +203,11 @@ rb_get_prepare_poll (struct rb *rb, chopstx_poll_cond_t *poll_desc)
const struct usart_stat *
usart_stat (uint8_t dev_no)
{
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
#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;
}
@@ -211,7 +215,11 @@ usart_stat (uint8_t dev_no)
static struct USART *
get_usart_dev (uint8_t dev_no)
{
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
#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;
}
@@ -219,7 +227,11 @@ get_usart_dev (uint8_t dev_no)
static struct rb *
get_usart_rb_h2a (uint8_t dev_no)
{
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
#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;
}
@@ -227,7 +239,11 @@ get_usart_rb_h2a (uint8_t dev_no)
static struct rb *
get_usart_rb_a2h (uint8_t dev_no)
{
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
#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;
}
@@ -235,7 +251,11 @@ get_usart_rb_a2h (uint8_t dev_no)
static struct chx_intr *
get_usart_intr (uint8_t dev_no)
{
if (dev_no < USART_DEVNO_START || dev_no > USART_DEVNO_END)
#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;
}

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

@@ -31,56 +31,14 @@
#include <chopstx.h>
#include <mcu/stm32.h>
#include <contrib/usart.h>
/* 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;
};
#include <contrib/usart-impl.h>
#include <contrib/usart-impl-f103.h>
#define USART2_BASE (APB1PERIPH_BASE + 0x4400)
#define USART3_BASE (APB1PERIPH_BASE + 0x4800)
#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)
#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)
static struct usart_stat usart2_stat;
static struct usart_stat usart3_stat;
@@ -112,20 +70,6 @@ static int usart3_tx_ready;
#define USART_DEVNO_START 2
#define USART_DEVNO_END 3
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 const struct usart usart_array[] =
{
{ USART2, &usart2_intr, INTR_REQ_USART2,
@@ -135,17 +79,6 @@ static const struct usart usart_array[] =
&usart3_stat, &usart3_rb_a2h, &usart3_rb_h2a, buf_usart3_rb_a2h,
buf_usart3_rb_h2a, &usart3_app_write_event, &usart3_tx_ready },
};
#define NUM_USART ((int)(sizeof (usart_array) / sizeof (struct usart)))
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))
/* We assume 36MHz f_PCLK */
static const struct brr_setting brr_table[] = {
@@ -169,393 +102,14 @@ static const struct brr_setting brr_table[] = {
};
#include "usart-common.c"
static void
usart_config_recv_enable (struct USART *USARTx, int on)
usart_rcc_setup (void)
{
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);
}
/* Enable USART2 and USART3 clocks, and strobe reset. */
RCC->APB1ENR |= ((1 << 18) | (1 << 17));
RCC->APB1RSTR = ((1 << 18) | (1 << 17));
RCC->APB1RSTR = 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->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;
}
#include "usart-common-f103.c"

View File

@@ -3,7 +3,7 @@
#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)
@@ -84,20 +84,6 @@ static int usart2_tx_ready;
#define USART_DEVNO_START 1
#define USART_DEVNO_END 2
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 const struct usart usart_array[] =
{
{ USART1, &usart1_intr, INTR_REQ_USART1,
@@ -109,17 +95,6 @@ static const struct usart usart_array[] =
buf_usart2_rb_h2a, &usart2_app_write_event, &usart2_tx_ready,
},
};
#define NUM_USART ((int)(sizeof (usart_array) / sizeof (struct usart)))
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))
/* We assume 40MHz f_CK */
static const struct brr_setting brr_table[] = {

View File

@@ -230,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.19
@set VERSION 2.3
@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, 2019 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
@@ -89,9 +89,10 @@ Indexes
@chapter Introduction
Chopstx is an RT thread library for ARM Cortex-M0, Cortex-M0plus,
Cortex-M3, Cortex-M4 with no FPU or DSP, and GNU/Linux emulation.
Specifically, it is used for STM32F030, MKL27Z, STM32F103, GD32F103,
STM32L432 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.
@@ -125,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
@@ -238,7 +239,7 @@ chx_handle_intr entries.
Obviously, this is suboptimal. It kills the hardware effort to
decrease interrupt latency.
I is certainly possible to support configurable vector table and/or
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

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, 2019
* Copyright (C) 2013, 2014, 2015, 2016, 2017, 2019, 2021
* Flying Stone Technology
* Author: NIIBE Yutaka <gniibe@fsij.org>
*
@@ -64,8 +64,7 @@ extern uint8_t __main_stack_end__;
#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)
@@ -114,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"
@@ -122,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
@@ -138,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"
@@ -148,9 +147,9 @@ 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"
@@ -158,7 +157,7 @@ entry (void)
"bl gpio_init\n\t"
/* Enable interrupts. */
#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
"mov r0, #0\n\t"
"movs r0, #0\n\t"
"msr BASEPRI, r0\n\t"
#endif
"cpsie i\n\t"
@@ -192,8 +191,8 @@ handler vector_table[] __attribute__ ((section(".startup.vectors"))) = {
#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 */,

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

@@ -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

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

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

@@ -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

@@ -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

@@ -15,6 +15,7 @@ LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld.m3
CSRC = sample.c
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes

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

View File

@@ -15,6 +15,7 @@ LDSCRIPT= sample.ld.m4
### LDSCRIPT= sample.ld.m3
CSRC = sample.c
ARCH=cortex-m
CHIP=stm32l4
USE_SYS = yes
USE_USART = yes

View File

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

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*/
}

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
}

59
mcu/gd32vf103.h Normal file
View File

@@ -0,0 +1,59 @@
struct RCU {
volatile uint32_t CTL;
volatile uint32_t CFG0;
volatile uint32_t INT;
volatile uint32_t APB2RST;
volatile uint32_t APB1RST;
volatile uint32_t AHBEN;
volatile uint32_t APB2EN;
volatile uint32_t APB1EN;
volatile uint32_t BDCTL;
volatile uint32_t RSTSCK;
volatile uint32_t AHBRST;
volatile uint32_t CFG1;
uint32_t rsv;
volatile uint32_t DSV;
};
static struct RCU *const RCU = (struct RCU *)0x40021000;
#define RCU_CTL_HXTALEN 0x00010000
#define RCU_CTL_HXTALSTB 0x00020000
#define RCU_CTL_PLLSTB 0x02000000
#define RCU_CTL_PLLEN 0x01000000
#define RCU_CTL_IRC8MEN 0x00000001
#define RCU_CFG0_ADC_MASK 0x0000c000
#define RCU_CFG0_AHB_APB1_APB2_MASK 0x00003ff0
#define RCU_CFG0_AHB_CKSYS_DIV1 0x00000000
#define RCU_CFG0_APB2_CKAHB_DIV1 0x00000000
#define RCU_CFG0_APB1_CKAHB_DIV2 0x00000400
#define RCU_CFG0_PLLSRC_PLLMF_MASK 0x203d0000
#define RCU_CFG0_PLL_MUL12 0x00280000
#define RCU_CFG0_PLLSRC_HXTAL 0x00010000
#define RCU_CFG0_SCS_MASK 0x00000003
#define RCU_CFG0_SCSS_PLL 0x00000008
#define RCU_CFG0_CKSYSSRC_PLL 0x00000002
#define RCU_CFG0_USBFSPSC_DIV2 0x00c00000
#define RCU_CFG1_PREDV0SEL_MASK 0x00010000
#define RCU_CFG1_PREDV0SEL_HXTAL 0x00000000
#define RCU_APB2_GPIOA 0x00000004
#define RCU_APB2_GPIOB 0x00000008
#define RCU_APB2_GPIOC 0x00000010
#define RCU_AHB_USBFS 0x00001000
/* Semantics is exactly same as STM32F103. */
struct GPIO {
volatile uint32_t CRL;
volatile uint32_t CRH;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t BRR;
volatile uint32_t LCKR;
};
static struct GPIO *const GPIOA = (struct GPIO *)0x40010800;
static struct GPIO *const GPIOB = (struct GPIO *)0x40010C00;
static struct GPIO *const GPIOC = (struct GPIO *)0x40011000;

1271
mcu/usb-gd32vf103.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -22,10 +22,26 @@
*/
/*
FIXME:
RESET handling
USB Shutdown
Use reply structure of its own
* This driver is intended to emulate USB full-speed device, which
* maximum packet size is 64.
*
* "USBIP" is actually URB over network (instead of USB packet over
* network), and its (current) naive protocol may expose possible
* demarcation problem, which never occurs in real host-device; In
* real host-device relationship, it is host side, which does
* composition/decomposition of URB to/from packets. In an
* implmentation of USB device with USBIP, it needs to be device side,
* which does composition/decomposition of URB to/from packets.
*
* In this implementation of USB driver, URB_DATA_SIZE is defined as
* (65544+10), because (major) target device intended is CCID. In the
* CCID specification, you can find the value 65544+10.
*/
/*
* FIXME:
* RESET handling
* USB Shutdown
* Use reply structure of its own
*/
#include <pthread.h>
@@ -96,7 +112,7 @@ struct urb {
struct urb *next;
struct urb *prev;
uint16_t remain;
uint32_t remain;
char *data_p;
pthread_t tid;
@@ -236,7 +252,67 @@ attach_device (char busid[32], size_t *len_p)
return (const char *)&usbip_usb_device;
}
#define URB_DATA_SIZE 65535
/*
* The number 65544 comes from ISO 7816-4, which defines data format
* of smartcard; CLS INS P1 P2 occupies four octets. Then, Lc-octet
* to represent size of command data block, then command data block.
* Lastly, Le-octet to represent size of response data block to be
* returned.
*
* /----- for CLS INS P1 P2
* |
* | /-- for Lc
* | |
* | | /-- for data block
* | | |
* | | | /-- for Le
* | | | |
* v v v v
* 4 + 3 + 65535 + 2 = 65544
*
* The number 10 comes from the CCID protocol specification; It is the
* header size of CCID. Besides, we can find the number 65544 in the
* CCID protocol specification, too.
*
* And... we have "AND ONE MORE" three times.
*
* We accept that, as Buddha did.
*
* There are different interpretations for the historical fact and the
* idiom. Most likely, nowadays, here, third-time is considered a
* strikeout, perhaps, due to popularity of baseball.
*/
#define URB_DATA_SIZE (65544+10+1+1+1)
/*
* The reasons why there are "+1" three times.
*
* Wrong (1): +1, because of confusion of data size maximum.
* Wrong (2): +1, because of confusion of the size for Le.
* Wrong (3): +1, because of keeping applying old patch.
*
* Something like this may occur, unfortunately, in our world.
*
* We need to consider the real case of the max buffer size for
* libusb_bulk_transfer with CCID (let us call this "CASE_MAX").
*
* (1) Although the data size maximum (for all cases) is 65536, it
* only occurs when Le size is zero. In the particular case of
* CASE_MAX, it is actually 65535. When just applying the max value
* 65536, +1 occurs.
*
* (2) Although maximum size to represent Le is 3, it only occurs when
* Lc size is zero. In the particular case of CASE_MAX, it is
* actually 2. When just applying the max value 3, +1 occurs.
*
* (3) Fedora keeps a old patch for Ominikey 3121. The patch was
* written when the value of CMD_BUF_SIZE in libccid was small for
* short extended APDU exchange. In the patch, it uses the value 11
* for the header size (instead of 10) of TPDU, for its special
* support. Historically, CMD_BUF_SIZE in libccid was updated
* to bigger value to handle extended APDU exchange. When just applying
* the old patch using 11, +1 occurs.
*/
struct usbip_msg_cmd {
uint32_t devid;
@@ -299,12 +375,14 @@ static int write_data_transaction (struct usb_control *usbc_p,
static int read_data_transaction (struct usb_control *usbc_p,
int ep_num, char *buf, uint16_t count);
#define USB_MAX_PACKET_SIZE 64 /* For USB fullspeed device. */
static int
hc_handle_control_urb (struct urb *urb)
{
int r;
uint16_t count;
uint16_t remain = urb->len;
uint32_t remain = urb->len;
uint64_t l;
if ((debug & DEBUG_USB))
@@ -325,10 +403,10 @@ hc_handle_control_urb (struct urb *urb)
while (r == 0)
{
if (remain > 64)
count = 64;
if (remain > USB_MAX_PACKET_SIZE)
count = USB_MAX_PACKET_SIZE;
else
count = remain;
count = (uint16_t)remain;
read (usbc_ep0.eventfd, &l, sizeof (l));
r = control_write_data_transaction (urb->data_p, count);
@@ -337,7 +415,7 @@ hc_handle_control_urb (struct urb *urb)
urb->data_p += count;
remain -= count;
if (count < 64)
if (count < USB_MAX_PACKET_SIZE)
break;
}
if (r >= 0)
@@ -353,10 +431,10 @@ hc_handle_control_urb (struct urb *urb)
while (1)
{
if (remain > 64)
count = 64;
if (remain > USB_MAX_PACKET_SIZE)
count = USB_MAX_PACKET_SIZE;
else
count = remain;
count = (uint16_t)remain;
read (usbc_ep0.eventfd, &l, sizeof (l));
r = control_read_data_transaction (urb->data_p, count);
@@ -368,7 +446,7 @@ hc_handle_control_urb (struct urb *urb)
remain -= r;
urb->data_p += r;
if (r < 64)
if (r < USB_MAX_PACKET_SIZE)
break;
}
@@ -516,10 +594,10 @@ hc_handle_data_urb (struct usb_control *usbc_p)
if ((debug & DEBUG_USB))
puts ("hc_hdu 0");
if (urb->remain > 64)
count = 64;
if (urb->remain > USB_MAX_PACKET_SIZE)
count = USB_MAX_PACKET_SIZE;
else
count = urb->remain;
count = (uint16_t)urb->remain;
if (urb->dir == USBIP_DIR_OUT)
{ /* Output from host to device. */
@@ -533,7 +611,7 @@ hc_handle_data_urb (struct usb_control *usbc_p)
urb->data_p += count;
urb->remain -= count;
if (urb->remain == 0 || count < 64)
if (urb->remain == 0 || count < USB_MAX_PACKET_SIZE)
{
size_t len = urb->len - urb->remain;
@@ -565,7 +643,7 @@ hc_handle_data_urb (struct usb_control *usbc_p)
urb->remain -= r;
urb->data_p += r;
if (urb->remain == 0 || r < 64)
if (urb->remain == 0 || r < USB_MAX_PACKET_SIZE)
{
size_t len = urb->len - urb->remain;
@@ -596,7 +674,7 @@ issue_get_desc (void)
{
struct urb *urb;
urb = malloc (sizeof (struct urb) + 64);
urb = malloc (sizeof (struct urb) + USB_MAX_PACKET_SIZE);
urb->next = urb->prev = urb;
@@ -606,14 +684,14 @@ issue_get_desc (void)
urb->setup[3] = 1; /* Value H: desc_type */
urb->setup[4] = 0; /* Index */
urb->setup[5] = 0;
urb->setup[6] = 64; /* Length */
urb->setup[6] = USB_MAX_PACKET_SIZE; /* Length */
urb->setup[7] = 0;
urb->data_p = urb->data;
urb->seq = 0;
urb->devid = 0;
urb->dir = USBIP_DIR_IN;
urb->ep = 0;
urb->remain = urb->len = 64;
urb->remain = urb->len = USB_MAX_PACKET_SIZE;
hc_handle_control_urb (urb);
return urb;
}
@@ -729,7 +807,7 @@ usbip_handle_urb (uint32_t seq)
leave:
msg.cmd = htonl (REP_URB_SUBMIT);
msg.seq = htonl (urb->seq);
msg.seq = htonl (seq);
memset (&msg_rep, 0, sizeof (msg_rep));
msg_rep.status = htonl (r);

View File

@@ -1,7 +1,7 @@
# Chopstx make rules.
ifeq ($(EMULATION),)
CSRC += $(CHOPSTX)/entry.c
CSRC += $(CHOPSTX)/entry-$(ARCH).c
else
CSRC += $(CHOPSTX)/entry-gnu-linux.c
endif
@@ -62,22 +62,36 @@ LLIBDIR = $(patsubst %,-L%,$(LIBDIR))
VPATH = $(sort $(dir $(CSRC)))
###
ifeq ($(EMULATION),)
MCFLAGS = -mcpu=$(MCU)
ifeq ($(ARCH),riscv32)
# For now, just for my use of picolibc
INCDIR += /usr/local/picolibc/riscv64-unknown-elf/include
LIBDIR += /usr/local/picolibc/riscv64-unknown-elf/lib/rv32imac/ilp32
#
MCFLAGS = -march=rv32imac -mabi=ilp32
LDFLAGS = $(MCFLAGS) -nodefaultlibs -nostartfiles -lc -T$(LDSCRIPT) \
-Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch
else
MCFLAGS = -mcpu=$(MCU) -masm-syntax-unified
LDFLAGS = $(MCFLAGS) -nostartfiles -T$(LDSCRIPT) \
-Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--gc-sections
endif
else
MCFLAGS =
LDFLAGS =
DEFS += -D_GNU_SOURCE
endif
DEFS += -DARCH_HEADER='"chopstx-$(ARCH).h"' -DARCH_IMPL='"chopstx-$(ARCH).c"'
CFLAGS = $(MCFLAGS) $(OPT) $(CWARN) -Wa,-alms=$(BUILDDIR)/$(notdir $(<:.c=.lst)) $(DEFS)
LDFLAGS += $(LLIBDIR)
ifeq ($(EMULATION),)
ifeq ($(ARCH),riscv32)
else
CFLAGS += -mthumb -mno-thumb-interwork -DTHUMB
LDFLAGS += -mthumb -mno-thumb-interwork
endif
endif
CFLAGS += -MD -MP -MF .dep/$(@F).d

View File

@@ -38,11 +38,19 @@ enum DESCRIPTOR_TYPE
#define USB_SETUP_GET(req) ((req & REQUEST_DIR) != 0)
struct device_req {
uint8_t type;
uint8_t request;
uint16_t value;
uint16_t index;
uint16_t len;
union {
struct {
uint8_t type;
uint8_t request;
uint16_t value;
uint16_t index;
uint16_t len;
};
struct {
uint32_t w0;
uint32_t w1;
};
};
};
struct ctrl_data {
@@ -51,12 +59,27 @@ struct ctrl_data {
uint8_t require_zlp;
};
#ifdef MCU_GD32VF1
#define USB_REQUIRE_TXRX_INFO 1
#endif
#ifdef USB_REQUIRE_TXRX_INFO
struct epctl {
uint8_t *addr;
uint16_t len;
};
#endif
struct usb_dev {
uint8_t configuration;
uint8_t feature;
uint8_t state;
struct device_req dev_req;
struct ctrl_data ctrl_data;
#ifdef USB_REQUIRE_TXRX_INFO
struct epctl epctl_tx[3];
struct epctl epctl_rx[3];
#endif
};
enum {
@@ -152,7 +175,22 @@ void usb_lld_rx_enable_buf (int ep_num, void *buf, size_t len);
void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int rx_en, int tx_en);
void usb_lld_stall_tx (int ep_num);
void usb_lld_stall_rx (int ep_num);
#else
#elif defined(MCU_GD32VF1)
#define INTR_REQ_USB 86
#define INTR_REQ_USB_WAKEUP 61
void usb_lld_tx_enable_buf (struct usb_dev *dev, int ep_num, const void *buf, size_t len);
void usb_lld_rx_enable_buf (struct usb_dev *dev, int ep_num, void *buf, size_t len);
/* EP_TYPE[1:0] EndPoint TYPE */
#define EP_CONTROL 0x00
#define EP_ISOCHRONOUS 0x01
#define EP_BULK 0x02
#define EP_INTERRUPT 0x03
void usb_lld_setup_endp (struct usb_dev *dev, int ep_num, int ep_type, int rx_en, int tx_en);
void usb_lld_stall_tx (struct usb_dev *dev, int ep_num);
void usb_lld_stall_rx (struct usb_dev *dev, int ep_num);
#else /* STM32L4 or STM32F103/GD32F103 */
#if defined(MCU_STM32L4)
#define INTR_REQ_USB 67
#else