diff --git a/CHANGELOG.md b/CHANGELOG.md index f2240a5ee..ee88082c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12 | Date | Version | Comment | Link | |:----:|:-------:|:--------|:----:| +| 06.01.2024 | 1.9.2.8 | :sparkles: add timer-capture mode to General Purpose Timer (GPTMR); :warning: remove "single-shot" mode, change control register layout | [#759](https://github.com/stnolting/neorv32/pull/759) | | 19.12.2023 | 1.9.2.7 | minor rtl code cleanups, edits and optimization; :lock: reset `mtvec`, `mepc` and `dpc` CSRs to CPU boot address (`CPU_BOOT_ADDR` CPU generic) | [#755](https://github.com/stnolting/neorv32/pull/755) | | 19.12.2023 | 1.9.2.6 | rework FIFO component fixing problems with inferring block RAM | [#754](https://github.com/stnolting/neorv32/pull/754) | | 11.12.2023 | 1.9.2.5 | clean-up software framework | [#752](https://github.com/stnolting/neorv32/pull/752) | diff --git a/README.md b/README.md index c6ee3308f..8e8f680aa 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ allows booting application code via UART or from external SPI flash **Timers and Counters** * 64-bit machine timer ([MTIME](https://stnolting.github.io/neorv32/#_machine_system_timer_mtime)), RISC-V spec. compatible -* 32-bit general purpose timer ([GPTMR](https://stnolting.github.io/neorv32/#_general_purpose_timer_gptmr)) +* 32-bit general purpose timer ([GPTMR](https://stnolting.github.io/neorv32/#_general_purpose_timer_gptmr)) with capture input * watchdog timer ([WDT](https://stnolting.github.io/neorv32/#_watchdog_timer_wdt)) **Input / Output** diff --git a/docs/datasheet/soc.adoc b/docs/datasheet/soc.adoc index 33d3e0947..9e810e9a9 100644 --- a/docs/datasheet/soc.adoc +++ b/docs/datasheet/soc.adoc @@ -36,7 +36,7 @@ image::neorv32_processor.png[align=center] * _optional_ custom functions subsystem for custom co-processor extensions (<<_custom_functions_subsystem_cfs,**CFS**>>) * _optional_ NeoPixel(TM)/WS2812-compatible smart LED interface (<<_smart_led_interface_neoled,**NEOLED**>>) * _optional_ external interrupt controller with up to 32 channels (<<_external_interrupt_controller_xirq,**XIRQ**>>) -* _optional_ general purpose 32-bit timer (<<_general_purpose_timer_gptmr,**GPTMR**>>) +* _optional_ general purpose 32-bit timer (<<_general_purpose_timer_gptmr,**GPTMR**>>) with capture input * _optional_ execute in-place module (<<_execute_in_place_module_xip,**XIP**>>) * _optional_ 1-wire serial interface controller (<<_one_wire_serial_interface_controller_onewire,**ONEWIRE**>>), compatible to the 1-wire standard * _optional_ autonomous direct memory access controller (<<_direct_memory_access_controller_dma,**DMA**>>) @@ -150,6 +150,8 @@ Some interfaces (like the TWI and the 1-Wire bus) require tri-state drivers in t | `cfs_out_o` | 32 | out | custom CFS output signal conduit 4+^| **<<_smart_led_interface_neoled>>** | `neoled_o` | 1 | out | asynchronous serial data output +4+^| **<<_general_purpose_timer_gptmr>>** +| `gptmr_trig_i` | 1 | in | timer capture input 4+^| **<<_external_interrupt_controller_xirq>>** | `xirq_i` | 32 | in | external interrupt requests 4+^| **RISC-V Machine-Mode <<_processor_interrupts>>** diff --git a/docs/datasheet/soc_gptmr.adoc b/docs/datasheet/soc_gptmr.adoc index cae926563..de26e73b3 100644 --- a/docs/datasheet/soc_gptmr.adoc +++ b/docs/datasheet/soc_gptmr.adoc @@ -5,25 +5,27 @@ [cols="<3,<3,<4"] [frame="topbot",grid="none"] |======================= -| Hardware source file(s): | neorv32_gptmr.vhd | +| Hardware source file(s): | neorv32_gptmr.vhd | | Software driver file(s): | neorv32_gptmr.c | | | neorv32_gptmr.h | -| Top entity port: | none | -| Configuration generics: | `IO_GPTMR_EN` | implement general purpose timer when `true` +| Top entity port: | `gptmr_trig_i` | timer capture input +| Configuration generics: | `IO_GPTMR_EN` | implement general purpose timer when `true` | CPU interrupts: | fast IRQ channel 12 | timer interrupt (see <<_processor_interrupts>>) |======================= -**Theory of Operation** +**Overview** -The general purpose timer module provides a simple yet universal 32-bit timer. The timer is implemented if -`IO_GPTMR_EN` top generic is set `true`. It provides a 32-bit counter register (`COUNT`) and a 32-bit threshold -register (`THRES`). An interrupt is generated whenever the value of the counter registers matches the one from -threshold register. +The general purpose timer module implements a simple yet universal 32-bit timer. It is implemented if the processor's +`IO_GPTMR_EN` top generic is set `true`. The timer provides a pre-scaled counter register that can trigger an interrupt +when reaching a programmable threshold value. Additionally, a timer-capture feature is implemented that copies the current +counter value to a dedicated register if a programmable edge occurs at the `gptmr_trig_i` input signal. -The timer is enabled by setting the `GPTMR_CTRL_EN` bit in the device's control register `CTRL`. The `COUNT` -register will start incrementing at a programmable rate, which scales the main processor clock. The -pre-scaler value is configured via the three `GPTMR_CTRL_PRSCx` control register bits: +Four interface registers are available: a control register (`CTRL`), a 32-bit counter register (`COUNT`), a 32-bit +threshold register (`THRES`) and a 32-bit read-only capture register (`CAPTURE`). The timer is globally enabled by setting the +`GPTMR_CTRL_EN` bit in the device's control register `CTRL`. When the timer is enable the `COUNT` register will start +incrementing at a programmable rate, which scales the main processor clock. The pre-scaler value is configured via the +three `GPTMR_CTRL_PRSCx` control register bits: .GPTMR prescaler configuration [cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"] @@ -33,21 +35,39 @@ pre-scaler value is configured via the three `GPTMR_CTRL_PRSCx` control register | Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096 |======================= -The timer provides two operation modes that are configured via the `GPTMR_CTRL_MODE` control register bit: -.If `GPTMR_CTRL_MODE` is cleared (`0`) the timer operates in _single-shot mode_. As soon as `COUNT` matches -`THRES` an interrupt request is generated and the timer stops operation (i.e. it stops incrementing) -.If `GPTMR_CTRL_MODE` is set (`1`) the timer operates in _continuous mode_. When `COUNT` matches `THRES` an interrupt -request is generated and `COUNT` is automatically reset to all-zero before continuing to increment. - [NOTE] Disabling the timer will not clear the `COUNT` register. However, it can be manually reset at any time by writing zero to it. -**Timer Interrupt** +**Interval Timer** + +Whenever the counter register `COUNT` reaches the programmable threshold value `THRES` the counter register +is reset to zero and the _timer-match_ flag `GPTMR_CTRL_TRIGM` gets set. This flag has to be cleared manually +by writing zero to it. Optionally, an interrupt can be triggered if the `GPTMR_CTRL_IRQM` bit is set. + + +**Timer Capture** + +In addition to the the internal timer, the GPTMR provides a timer-capture feature. Whenever an edge is detected +at the `gptmr_trig_i` input signal the current `COUNT` value is copied to the read-only `CAPTURE` register and the +_capture-trigger_ flag `GPTMR_CTRL_TRIGC` gets set. This flag has to be cleared manually by writing zero to it. +Optionally, an interrupt can be triggered if the `GPTMR_CTRL_IRQC` bit is set. -The GPTMR interrupt is triggered when the timer is enabled and `COUNT` matches `THRES`. The interrupt -remains pending inside the CPU until it explicitly cleared by writing zero to the according <<_mip>> CSR bit. +The triggering edge can be a rising-edge (if `GPTMR_CTRL_RISE` is set), a falling-edge (if `GPTMR_CTRL_FALL` is +set) or even both. By default, the `gptmr_trig_i` is sampled two times at the processor clock for checking for +edges. This simple edge detection is sufficient for trigger signals that are generated by (on-chip) digital logic. + +For sampling chip-external signals an optional filtering mode is available that can be enabled by the +`GPTMR_CTRL_FILTER` bit. If this bit is set, the `gptmr_trig_i` is sampled at a reduced clock speed (1/4 of the +processor clock) and the signal has to be stable for at lest 4 sample clock in order to be considered high or low. +This stabilized signal is then fed to the edge detection logic. + + +.Timer Interrupt +[NOTE] +Once triggered, the timer interrupt remains pending within the CPU until it explicitly cleared by writing zero +to the according <<_mip>> CSR bit. **Register Map** @@ -57,10 +77,17 @@ remains pending inside the CPU until it explicitly cleared by writing zero to th [options="header",grid="all"] |======================= | Address | Name [C] | Bit(s), Name [C] | R/W | Function -.4+<| `0xfffff100` .4+<| `CTRL` <|`0` `GPTMR_CTRL_EN` ^| r/w <| Timer enable flag - <|`3:1` `GPTMR_CTRL_PRSC2 : GPTMR_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select - <|`4` `GPTMR_CTRL_MODE` ^| r/w <| Counter mode: `0`=single-shot, `1`=continuous - <|`31:5` - ^| r/- <| _reserved_, read as zero -| `0xfffff104` | `THRES` |`31:0` | r/w | Threshold value register -| `0xfffff108` | `COUNT` |`31:0` | r/w | Counter register +.10+<| `0xfffff100` .10+<| `CTRL` <|`0` `GPTMR_CTRL_EN` ^| r/w <| Timer enable flag + <|`3:1` `GPTMR_CTRL_PRSC2 : GPTMR_CTRL_PRSC0` ^| r/w <| 3-bit clock prescaler select + <|`4` `GPTMR_CTRL_IRQM` ^| r/w <| Enable interrupt on timer-match + <|`5` `GPTMR_CTRL_IRQC` ^| r/w <| Enable interrupt on capture-trigger + <|`6` `GPTMR_CTRL_RISE` ^| r/w <| Capture on rising edge + <|`7` `GPTMR_CTRL_FALL` ^| r/w <| Capture on falling edge + <|`8` `GPTMR_CTRL_FILTER` ^| r/w <| Filter capture input + <|`29:9` - ^| r/- <| _reserved_, read as zero + <|`30` `GPTMR_CTRL_TRIGM` ^| r/c <| Timer-match has fired, cleared by writing 0 + <|`31` `GPTMR_CTRL_TRIGC` ^| r/c <| Capture-trigger has fired, cleared by writing 0 +| `0xfffff104` | `THRES` |`31:0` | r/w | Threshold value register +| `0xfffff108` | `COUNT` |`31:0` | r/w | Counter register +| `0xfffff10C` | `CAPTURE` |`31:0` | r/- | Capture register |======================= diff --git a/docs/figures/neorv32_processor.png b/docs/figures/neorv32_processor.png index cb23f64ef..437bf2b9b 100644 Binary files a/docs/figures/neorv32_processor.png and b/docs/figures/neorv32_processor.png differ diff --git a/rtl/core/neorv32_gptmr.vhd b/rtl/core/neorv32_gptmr.vhd index 5b4270345..33d9ddb6a 100644 --- a/rtl/core/neorv32_gptmr.vhd +++ b/rtl/core/neorv32_gptmr.vhd @@ -1,14 +1,13 @@ -- ################################################################################################# -- # << NEORV32 - General Purpose Timer (GPTMR) >> # -- # ********************************************************************************************* # --- # 32-bit timer with configurable clock prescaler. The timer fires an interrupt whenever the # --- # counter register value reaches the programmed threshold value. The timer can operate in # --- # single-shot mode (count until it reaches THRESHOLD and stop) or in continuous mode (count # --- # until it reaches THRESHOLD and auto-reset). # +-- # 32-bit timer with configurable clock prescaler, timer-threshold match and timer-capture # +-- # features. An interrupt can be triggered if the counter reaches a programmable threshold or if # +-- # a programmable edge is detects at the capture input. # -- # ********************************************************************************************* # -- # BSD 3-Clause License # -- # # --- # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. # -- # # -- # Redistribution and use in source and binary forms, with or without modification, are # -- # permitted provided that the following conditions are met: # @@ -52,31 +51,56 @@ entity neorv32_gptmr is bus_rsp_o : out bus_rsp_t; -- bus response clkgen_en_o : out std_ulogic; -- enable clock generator clkgen_i : in std_ulogic_vector(7 downto 0); - irq_o : out std_ulogic -- timer match interrupt + irq_o : out std_ulogic; -- timer match interrupt + capture_i : in std_ulogic -- capture input ); end neorv32_gptmr; architecture neorv32_gptmr_rtl of neorv32_gptmr is -- control register -- - constant ctrl_en_c : natural := 0; -- r/w: timer enable - constant ctrl_prsc0_c : natural := 1; -- r/w: clock prescaler select bit 0 - constant ctrl_prsc1_c : natural := 2; -- r/w: clock prescaler select bit 1 - constant ctrl_prsc2_c : natural := 3; -- r/w: clock prescaler select bit 2 - constant ctrl_mode_c : natural := 4; -- r/w: mode (0=single-shot, 1=continuous) + constant ctrl_en_c : natural := 0; -- r/w: GPTMR enable + constant ctrl_prsc0_c : natural := 1; -- r/w: clock prescaler select bit 0 + constant ctrl_prsc1_c : natural := 2; -- r/w: clock prescaler select bit 1 + constant ctrl_prsc2_c : natural := 3; -- r/w: clock prescaler select bit 2 + constant ctrl_irqm_c : natural := 4; -- r/w: enable interrupt on timer match + constant ctrl_irqc_c : natural := 5; -- r/w: enable interrupt on capture trigger + constant ctrl_rise_c : natural := 6; -- r/w: capture on rising edge + constant ctrl_fall_c : natural := 7; -- r/w: capture on falling edge + constant ctrl_filter_c : natural := 8; -- r/w: filter capture input signal -- - signal ctrl : std_ulogic_vector(4 downto 0); + constant ctrl_trigm_c : natural := 30; -- r/c: timer-match has fired, cleared by writing 0 + constant ctrl_trigc_c : natural := 31; -- r/c: capture-trigger has fired, cleared by writing 0 + -- + signal ctrl : std_ulogic_vector(8 downto 0); + + -- trigger flags -- + signal trig_match, trig_capture : std_ulogic; -- timer core -- type timer_t is record - count : std_ulogic_vector(31 downto 0); -- counter register - thres : std_ulogic_vector(31 downto 0); -- threshold value - tick : std_ulogic; -- clock generator tick - match : std_ulogic; -- count == thres - cnt_we : std_ulogic; -- write access to count + count : std_ulogic_vector(31 downto 0); -- counter register + thres : std_ulogic_vector(31 downto 0); -- threshold register + tick : std_ulogic; -- clock generator tick + match : std_ulogic; -- count == thres + match_ff : std_ulogic; + trigger : std_ulogic; -- match trigger (single-shot) + cnt_we : std_ulogic; -- write access to count register end record; signal timer : timer_t; + -- sampling and capture logic -- + type capture_t is record + sync : std_ulogic_vector(1 downto 0); -- synchronizer (prevent metastability) + sreg : std_ulogic_vector(3 downto 0); -- input sampling + state : std_ulogic_vector(1 downto 0); -- state buffer to detect edges + count : std_ulogic_vector(31 downto 0); -- capture register + trigger : std_ulogic; -- edge detector has been triggered + rising : std_ulogic; -- rising-edge detector + falling : std_ulogic; -- falling-edge detector + end record; + signal capture : capture_t; + begin -- Bus Access ----------------------------------------------------------------------------- @@ -87,28 +111,43 @@ begin bus_rsp_o.ack <= '0'; bus_rsp_o.err <= '0'; bus_rsp_o.data <= (others => '0'); - timer.cnt_we <= '0'; ctrl <= (others => '0'); + trig_match <= '0'; + trig_capture <= '0'; + timer.cnt_we <= '0'; timer.thres <= (others => '0'); elsif rising_edge(clk_i) then - -- bus handshake -- + -- defaults -- bus_rsp_o.ack <= bus_req_i.stb; - bus_rsp_o.err <= '0'; + bus_rsp_o.err <= '0'; -- no access error possible bus_rsp_o.data <= (others => '0'); + timer.cnt_we <= '0'; - -- defaults -- - timer.cnt_we <= '0'; + -- trigger flags -- + trig_match <= ctrl(ctrl_en_c) and (trig_match or timer.trigger); + trig_capture <= ctrl(ctrl_en_c) and (trig_capture or capture.trigger); + -- actual bus access -- if (bus_req_i.stb = '1') then -- write access -- if (bus_req_i.rw = '1') then if (bus_req_i.addr(3 downto 2) = "00") then -- control register - ctrl(ctrl_en_c) <= bus_req_i.data(ctrl_en_c); - ctrl(ctrl_prsc0_c) <= bus_req_i.data(ctrl_prsc0_c); - ctrl(ctrl_prsc1_c) <= bus_req_i.data(ctrl_prsc1_c); - ctrl(ctrl_prsc2_c) <= bus_req_i.data(ctrl_prsc2_c); - ctrl(ctrl_mode_c) <= bus_req_i.data(ctrl_mode_c); + ctrl(ctrl_en_c) <= bus_req_i.data(ctrl_en_c); + ctrl(ctrl_prsc0_c) <= bus_req_i.data(ctrl_prsc0_c); + ctrl(ctrl_prsc1_c) <= bus_req_i.data(ctrl_prsc1_c); + ctrl(ctrl_prsc2_c) <= bus_req_i.data(ctrl_prsc2_c); + ctrl(ctrl_irqm_c) <= bus_req_i.data(ctrl_irqm_c); + ctrl(ctrl_irqc_c) <= bus_req_i.data(ctrl_irqc_c); + ctrl(ctrl_rise_c) <= bus_req_i.data(ctrl_rise_c); + ctrl(ctrl_fall_c) <= bus_req_i.data(ctrl_fall_c); + ctrl(ctrl_filter_c) <= bus_req_i.data(ctrl_filter_c); + if (bus_req_i.data(ctrl_trigm_c) = '0') then -- clear by writing zero + trig_match <= '0'; + end if; + if (bus_req_i.data(ctrl_trigc_c) = '0') then -- clear by writing zero + trig_capture <= '0'; + end if; end if; if (bus_req_i.addr(3 downto 2) = "01") then -- threshold register timer.thres <= bus_req_i.data; @@ -121,15 +160,24 @@ begin else case bus_req_i.addr(3 downto 2) is when "00" => -- control register - bus_rsp_o.data(ctrl_en_c) <= ctrl(ctrl_en_c); - bus_rsp_o.data(ctrl_prsc0_c) <= ctrl(ctrl_prsc0_c); - bus_rsp_o.data(ctrl_prsc1_c) <= ctrl(ctrl_prsc1_c); - bus_rsp_o.data(ctrl_prsc2_c) <= ctrl(ctrl_prsc2_c); - bus_rsp_o.data(ctrl_mode_c) <= ctrl(ctrl_mode_c); + bus_rsp_o.data(ctrl_en_c) <= ctrl(ctrl_en_c); + bus_rsp_o.data(ctrl_prsc0_c) <= ctrl(ctrl_prsc0_c); + bus_rsp_o.data(ctrl_prsc1_c) <= ctrl(ctrl_prsc1_c); + bus_rsp_o.data(ctrl_prsc2_c) <= ctrl(ctrl_prsc2_c); + bus_rsp_o.data(ctrl_irqm_c) <= ctrl(ctrl_irqm_c); + bus_rsp_o.data(ctrl_irqc_c) <= ctrl(ctrl_irqc_c); + bus_rsp_o.data(ctrl_rise_c) <= ctrl(ctrl_rise_c); + bus_rsp_o.data(ctrl_fall_c) <= ctrl(ctrl_fall_c); + bus_rsp_o.data(ctrl_filter_c) <= ctrl(ctrl_filter_c); + -- + bus_rsp_o.data(ctrl_trigm_c) <= trig_match; + bus_rsp_o.data(ctrl_trigc_c) <= trig_capture; when "01" => -- threshold register bus_rsp_o.data <= timer.thres; - when others => -- counter register + when "10" => -- counter register bus_rsp_o.data <= timer.count; + when others => -- capture register + bus_rsp_o.data <= capture.count; end case; end if; @@ -143,27 +191,27 @@ begin counter_core: process(rstn_i, clk_i) begin if (rstn_i = '0') then - timer.count <= (others => '0'); + timer.count <= (others => '0'); + timer.match_ff <= '0'; elsif rising_edge(clk_i) then if (timer.cnt_we = '1') then -- write access - timer.count <= bus_req_i.data; -- data_i will stay unchanged for min. 1 cycle after WREN has returned to low again + timer.count <= bus_req_i.data; -- data_i will remain stable for at least 1 cycle after WREN has returned to low elsif (ctrl(ctrl_en_c) = '1') and (timer.tick = '1') then -- enabled and clock tick - if (timer.match = '1') then - if (ctrl(ctrl_mode_c) = '1') then -- reset counter if continuous mode - timer.count <= (others => '0'); - end if; + if (timer.match = '1') then -- timer-match + timer.count <= (others => '0'); else timer.count <= std_ulogic_vector(unsigned(timer.count) + 1); end if; end if; + timer.match_ff <= timer.match; end if; end process counter_core; -- counter = threshold? -- timer.match <= '1' when (timer.count = timer.thres) else '0'; - -- clock generator enable -- - clkgen_en_o <= ctrl(ctrl_en_c); + -- match edge detector -- + timer.trigger <= '1' when (timer.match_ff = '0') and (timer.match = '1') else '0'; -- clock select -- clock_select: process(rstn_i, clk_i) @@ -175,13 +223,68 @@ begin end if; end process clock_select; - -- interrupt -- + -- clock generator enable -- + clkgen_en_o <= ctrl(ctrl_en_c); + + + -- Capture Control ------------------------------------------------------------------------ + -- ------------------------------------------------------------------------------------------- + signal_capture: process(rstn_i, clk_i) + begin + if (rstn_i = '0') then + capture.sync <= (others => '0'); + capture.sreg <= (others => '0'); + capture.state <= (others => '0'); + capture.count <= (others => '0'); + elsif rising_edge(clk_i) then + -- synchronizer - prevent metastability -- + capture.sync <= capture.sync(0) & capture_i; + + -- sample shift register, running at reduced sample rate -- + if (clkgen_i(clk_div4_c) = '1') then + capture.sreg <= capture.sreg(2 downto 0) & capture.sync(1); + end if; + + -- sample state buffer -- + capture.state(1) <= capture.state(0); + if (ctrl(ctrl_filter_c) = '0') then -- no filter, use synchronized input + capture.state(0) <= capture.sync(1); + else -- active filter: change state only if input is stable for 4 sample clocks + if (capture.sreg = "1111") then + capture.state(0) <= '1'; + elsif (capture.sreg = "0000") then + capture.state(0) <= '0'; + else + capture.state(0) <= capture.state(0); + end if; + end if; + + -- timer capture -- + if (ctrl(ctrl_en_c) = '1') and (capture.trigger = '1') then + capture.count <= timer.count; + end if; + end if; + end process signal_capture; + + -- edge detectors -- + capture.rising <= '1' when (capture.state = "01") else '0'; + capture.falling <= '1' when (capture.state = "10") else '0'; + + -- capture trigger -- + capture.trigger <= (ctrl(ctrl_rise_c) and capture.rising) or -- rising-edge trigger + (ctrl(ctrl_fall_c) and capture.falling); -- falling-edge trigger + + + -- Interrupt Generator -------------------------------------------------------------------- + -- ------------------------------------------------------------------------------------------- irq_generator: process(rstn_i, clk_i) begin if (rstn_i = '0') then irq_o <= '0'; elsif rising_edge(clk_i) then - irq_o <= ctrl(ctrl_en_c) and timer.match; + irq_o <= ctrl(ctrl_en_c) and + ((ctrl(ctrl_irqm_c) and timer.trigger) or -- timer-match interrupt + (ctrl(ctrl_irqc_c) and capture.trigger)); -- capture interrupt end if; end process irq_generator; diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index 9df1f576d..c24c28cbe 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -3,7 +3,7 @@ -- # ********************************************************************************************* # -- # BSD 3-Clause License # -- # # --- # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. # -- # # -- # Redistribution and use in source and binary forms, with or without modification, are # -- # permitted provided that the following conditions are met: # @@ -56,7 +56,7 @@ package neorv32_package is -- Architecture Constants ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090207"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090208"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width @@ -865,7 +865,7 @@ package neorv32_package is -- Advanced memory control signals -- fence_o : out std_ulogic; fencei_o : out std_ulogic; - -- XIP (execute in place via SPI) signals (available if IO_XIP_EN = true) -- + -- XIP (execute in-place via SPI) signals (available if IO_XIP_EN = true) -- xip_csn_o : out std_ulogic; xip_clk_o : out std_ulogic; xip_dat_i : in std_ulogic := 'L'; @@ -908,6 +908,8 @@ package neorv32_package is cfs_out_o : out std_ulogic_vector(IO_CFS_OUT_SIZE-1 downto 0); -- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) -- neoled_o : out std_ulogic; + -- GPTMR timer capture (available if IO_GPTMR_EN = true) -- + gptmr_trig_i : in std_ulogic := 'L'; -- External platform interrupts (available if XIRQ_NUM_CH > 0) -- xirq_i : in std_ulogic_vector(31 downto 0) := (others => 'L'); -- CPU Interrupts -- diff --git a/rtl/core/neorv32_top.vhd b/rtl/core/neorv32_top.vhd index 0bafc528c..c5f26c187 100644 --- a/rtl/core/neorv32_top.vhd +++ b/rtl/core/neorv32_top.vhd @@ -8,7 +8,7 @@ -- # ********************************************************************************************* # -- # BSD 3-Clause License # -- # # --- # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. # -- # # -- # Redistribution and use in source and binary forms, with or without modification, are # -- # permitted provided that the following conditions are met: # @@ -241,6 +241,9 @@ entity neorv32_top is -- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) -- neoled_o : out std_ulogic; -- async serial data line + -- GPTMR timer capture (available if IO_GPTMR_EN = true) -- + gptmr_trig_i : in std_ulogic := 'L'; -- capture trigger + -- External platform interrupts (available if XIRQ_NUM_CH > 0) -- xirq_i : in std_ulogic_vector(31 downto 0) := (others => 'L'); -- IRQ channels @@ -1375,7 +1378,8 @@ begin bus_rsp_o => iodev_rsp(IODEV_GPTMR), clkgen_en_o => cg_en.gptmr, clkgen_i => clk_gen, - irq_o => firq.gptmr + irq_o => firq.gptmr, + capture_i => gptmr_trig_i ); end generate; diff --git a/sim/neorv32_tb.vhd b/sim/neorv32_tb.vhd index 1df10ddcb..7d66650e2 100644 --- a/sim/neorv32_tb.vhd +++ b/sim/neorv32_tb.vhd @@ -7,7 +7,7 @@ -- # ********************************************************************************************* # -- # BSD 3-Clause License # -- # # --- # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. # -- # # -- # Redistribution and use in source and binary forms, with or without modification, are # -- # permitted provided that the following conditions are met: # @@ -377,6 +377,8 @@ begin cfs_out_o => open, -- custom CFS outputs -- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) -- neoled_o => open, -- async serial data line + -- GPTMR timer capture (available if IO_GPTMR_EN = true) -- + gptmr_trig_i => gpio(63), -- capture trigger -- External platform interrupts (available if XIRQ_NUM_CH > 0) -- xirq_i => gpio(31 downto 0), -- IRQ channels -- CPU Interrupts -- diff --git a/sim/simple/neorv32_tb.simple.vhd b/sim/simple/neorv32_tb.simple.vhd index 2779e1885..d8513278c 100644 --- a/sim/simple/neorv32_tb.simple.vhd +++ b/sim/simple/neorv32_tb.simple.vhd @@ -7,7 +7,7 @@ -- # ********************************************************************************************* # -- # BSD 3-Clause License # -- # # --- # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. # -- # # -- # Redistribution and use in source and binary forms, with or without modification, are # -- # permitted provided that the following conditions are met: # @@ -325,6 +325,8 @@ begin cfs_out_o => open, -- custom CFS outputs -- NeoPixel-compatible smart LED interface (available if IO_NEOLED_EN = true) -- neoled_o => open, -- async serial data line + -- GPTMR timer capture (available if IO_GPTMR_EN = true) -- + gptmr_trig_i => gpio(63), -- capture trigger -- External platform interrupts (available if XIRQ_NUM_CH > 0) -- xirq_i => gpio(31 downto 0), -- IRQ channels -- CPU Interrupts -- diff --git a/sw/example/demo_dma/main.c b/sw/example/demo_dma/main.c index 57a036cab..a8f8b017d 100644 --- a/sw/example/demo_dma/main.c +++ b/sw/example/demo_dma/main.c @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -223,8 +223,8 @@ int main() { // configure GPTMR neorv32_gptmr_setup(CLK_PRSC_2, // GPTM clock = 1/2 main clock - 0, // single-shot mode - 2000); // counter to threshold for triggering IRQ + 4096, // counter threshold for triggering IRQ + 1); // enable timer-match interrupt // configure transfer type cmd = DMA_CMD_B2B | // read source in byte quantities, write destination in byte quantities @@ -283,5 +283,6 @@ void show_arrays(void) { void dma_firq_handler(void) { neorv32_cpu_csr_clr(CSR_MIP, 1 << DMA_FIRQ_PENDING); // clear/ack pending FIRQ + neorv32_gptmr_disable(); // disable GPTMR neorv32_uart0_printf("<>\n"); } diff --git a/sw/example/demo_gptmr/main.c b/sw/example/demo_gptmr/main.c index b9437ac41..8c617f0de 100644 --- a/sw/example/demo_gptmr/main.c +++ b/sw/example/demo_gptmr/main.c @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -36,7 +36,7 @@ /**********************************************************************//** * @file demo_gptmr/main.c * @author Stephan Nolting - * @brief Simple GPTMR usage example. + * @brief Simple GPTMR timer-match interrupt example. **************************************************************************/ #include @@ -63,7 +63,7 @@ void gptmr_firq_handler(void); * @return Should not return; **************************************************************************/ int main() { - + // setup NEORV32 runtime environment (for trap handling) neorv32_rte_setup(); @@ -79,7 +79,7 @@ int main() { // Intro neorv32_uart0_puts("General purpose timer (GPTMR) demo Program.\n" - "Toggles GPIO.output(0) at 1Hz using the GPTMR interrupt.\n\n"); + "Toggles GPIO.output(0) at 1Hz using the GPTMR timer-match interrupt.\n\n"); // clear GPIO output port @@ -89,8 +89,8 @@ int main() { // install GPTMR interrupt handler neorv32_rte_handler_install(GPTMR_RTE_ID, gptmr_firq_handler); - // configure timer for 1Hz ticks in continuous mode (with clock divisor = 8) - neorv32_gptmr_setup(CLK_PRSC_8, 1, NEORV32_SYSINFO->CLK / (8 * 2)); + // configure timer for 1Hz ticks with clock divisor = 8 and enable timer-match interrupt + neorv32_gptmr_setup(CLK_PRSC_8, NEORV32_SYSINFO->CLK / (8 * 2), 1); // enable interrupt neorv32_cpu_csr_clr(CSR_MIP, 1 << GPTMR_FIRQ_PENDING); // make sure there is no GPTMR IRQ pending already diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index 3fec423c8..d6a5cd721 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -1555,8 +1555,8 @@ int main() { // enable GPTMR FIRQ neorv32_cpu_irq_enable(GPTMR_FIRQ_ENABLE); - // configure timer IRQ for one-shot mode after CLK_PRSC_2*2=4 clock cycles - neorv32_gptmr_setup(CLK_PRSC_2, 0, 2); + // match-interrupt after CLK_PRSC_2*THRESHOLD = 2*2 = 8 clock cycles + neorv32_gptmr_setup(CLK_PRSC_2, 2, 1); // wait for interrupt asm volatile ("nop"); @@ -1564,13 +1564,18 @@ int main() { neorv32_cpu_csr_write(CSR_MIE, 0); - // check if IRQ - if (neorv32_cpu_csr_read(CSR_MCAUSE) == GPTMR_TRAP_CODE) { + if ((neorv32_cpu_csr_read(CSR_MCAUSE) == GPTMR_TRAP_CODE) && // correct interrupt? + (neorv32_gptmr_trigger_matched() == 1) && // IRQ caused by timer match? + (neorv32_gptmr_trigger_captured() == 0)) { // no capture trigger? test_ok(); } else { test_fail(); } + + // disable GPTMR + neorv32_gptmr_disable(); + } else { PRINT_STANDARD("[n.a.]\n"); diff --git a/sw/lib/include/neorv32_gptmr.h b/sw/lib/include/neorv32_gptmr.h index fc762f41c..ab0a40d27 100644 --- a/sw/lib/include/neorv32_gptmr.h +++ b/sw/lib/include/neorv32_gptmr.h @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -49,22 +49,29 @@ /**@{*/ /** GPTMR module prototype */ typedef volatile struct __attribute__((packed,aligned(4))) { - uint32_t CTRL; /**< offset 0: control register (#NEORV32_GPTMR_CTRL_enum) */ - uint32_t THRES; /**< offset 4: threshold register */ - uint32_t COUNT; /**< offset 8: counter register */ - const uint32_t reserved; /**< offset 12: reserved */ + uint32_t CTRL; /**< offset 0: control register (#NEORV32_GPTMR_CTRL_enum) */ + uint32_t THRES; /**< offset 4: threshold register */ + uint32_t COUNT; /**< offset 8: counter register */ + uint32_t CAPTURE; /**< offset 12: capture register */ } neorv32_gptmr_t; /** GPTMR module hardware access (#neorv32_gptmr_t) */ #define NEORV32_GPTMR ((neorv32_gptmr_t*) (NEORV32_GPTMR_BASE)) -/** GPTMR control/data register bits */ +/** GPTMR control register bits */ enum NEORV32_GPTMR_CTRL_enum { - GPTMR_CTRL_EN = 0, /**< GPTIMR control register(0) (r/w): Timer unit enable */ - GPTMR_CTRL_PRSC0 = 1, /**< GPTIMR control register(1) (r/w): Clock prescaler select bit 0 */ - GPTMR_CTRL_PRSC1 = 2, /**< GPTIMR control register(2) (r/w): Clock prescaler select bit 1 */ - GPTMR_CTRL_PRSC2 = 3, /**< GPTIMR control register(3) (r/w): Clock prescaler select bit 2 */ - GPTMR_CTRL_MODE = 4 /**< GPTIMR control register(4) (r/w): Timer mode: 0=single-shot mode, 1=continuous mode */ + GPTMR_CTRL_EN = 0, /**< GPTMR control register(0) (r/w): GPTMR enable */ + GPTMR_CTRL_PRSC0 = 1, /**< GPTMR control register(1) (r/w): Clock prescaler select bit 0 */ + GPTMR_CTRL_PRSC1 = 2, /**< GPTMR control register(2) (r/w): Clock prescaler select bit 1 */ + GPTMR_CTRL_PRSC2 = 3, /**< GPTMR control register(3) (r/w): Clock prescaler select bit 2 */ + GPTMR_CTRL_IRQM = 4, /**< GPTMR control register(4) (r/w): Enable interrupt on timer match */ + GPTMR_CTRL_IRQC = 5, /**< GPTMR control register(5) (r/w): Enable interrupt on capture trigger */ + GPTMR_CTRL_RISE = 6, /**< GPTMR control register(6) (r/w): Capture on rising edge; capture-mode only */ + GPTMR_CTRL_FALL = 7, /**< GPTMR control register(7) (r/w): Capture on falling edge; capture-mode only */ + GPTMR_CTRL_FILTER = 8, /**< GPTMR control register(8) (r/w): Filter capture input; capture-mode only */ + + GPTMR_CTRL_TRIGM = 30, /**< GPTMR control register(30) (r/c): Timer-match has fired, cleared by writing 0 */ + GPTMR_CTRL_TRIGC = 31, /**< GPTMR control register(31) (r/c): Capture-trigger has fired, cleared by writing 0 */ }; /**@}*/ @@ -73,11 +80,16 @@ enum NEORV32_GPTMR_CTRL_enum { * @name Prototypes **************************************************************************/ /**@{*/ -int neorv32_gptmr_available(void); -void neorv32_gptmr_setup(int prsc, int mode, uint32_t threshold); -void neorv32_gptmr_disable(void); -void neorv32_gptmr_enable(void); -void neorv32_gptmr_restart(void); +int neorv32_gptmr_available(void); +void neorv32_gptmr_setup(int prsc, uint32_t threshold, int match_irq); +void neorv32_gptmr_capture(int rising, int falling, int filter, int capture_irq); +void neorv32_gptmr_disable(void); +void neorv32_gptmr_enable(void); +int neorv32_gptmr_trigger_matched(void); +int neorv32_gptmr_trigger_captured(void); +void neorv32_gptmr_restart(void); +uint32_t neorv32_gptmr_counter_get(void); +uint32_t neorv32_gptmr_capture_get(void); /**@}*/ diff --git a/sw/lib/source/neorv32_gptmr.c b/sw/lib/source/neorv32_gptmr.c index 057517629..214023bfe 100644 --- a/sw/lib/source/neorv32_gptmr.c +++ b/sw/lib/source/neorv32_gptmr.c @@ -3,7 +3,7 @@ // # ********************************************************************************************* # // # BSD 3-Clause License # // # # -// # Copyright (c) 2023, Stephan Nolting. All rights reserved. # +// # Copyright (c) 2024, Stephan Nolting. All rights reserved. # // # # // # Redistribution and use in source and binary forms, with or without modification, are # // # permitted provided that the following conditions are met: # @@ -61,22 +61,42 @@ int neorv32_gptmr_available(void) { /**********************************************************************//** - * Enable and configure general purpose timer. + * Reset, enable and configure general purpose timer. * * @param[in] prsc Clock prescaler select (0..7). See #NEORV32_CLOCK_PRSC_enum. - * @param[in] mode 0=single-shot mode, 1=continuous mode - * @param[in] threshold Threshold value to trigger interrupt. + * @param[in] threshold Threshold value, counter will reset to zero when reaching this. + * @param[in] match_irq Fire interrupt when counter matches threshold value. **************************************************************************/ -void neorv32_gptmr_setup(int prsc, int mode, uint32_t threshold) { +void neorv32_gptmr_setup(int prsc, uint32_t threshold, int match_irq) { - NEORV32_GPTMR->CTRL = 0; // reset + NEORV32_GPTMR->CTRL = 0; // reset configuration NEORV32_GPTMR->THRES = threshold; NEORV32_GPTMR->COUNT = 0; // reset counter uint32_t tmp = 0; - tmp |= (uint32_t)(1 & 0x01) << GPTMR_CTRL_EN; - tmp |= (uint32_t)(prsc & 0x07) << GPTMR_CTRL_PRSC0; - tmp |= (uint32_t)(mode & 0x01) << GPTMR_CTRL_MODE; + tmp |= (uint32_t)(1 & 0x01) << GPTMR_CTRL_EN; + tmp |= (uint32_t)(prsc & 0x07) << GPTMR_CTRL_PRSC0; + tmp |= (uint32_t)(match_irq & 0x01) << GPTMR_CTRL_IRQM; + NEORV32_GPTMR->CTRL = tmp; +} + + +/**********************************************************************//** + * Configure timer capture feature. + * @note This function needs to be called after the general GPTMR setup #neorv32_gptmr_setup. + * + * @param[in] rising Capture on rising edge. + * @param[in] falling Capture on falling edge. + * @param[in] filter Enable filtering of capture input. + * @param[in] capture_irq Fire interrupt when on capture trigger. + **************************************************************************/ +void neorv32_gptmr_capture(int rising, int falling, int filter, int capture_irq) { + + uint32_t tmp = NEORV32_GPTMR->CTRL; + tmp |= (uint32_t)(rising & 0x01) << GPTMR_CTRL_RISE; + tmp |= (uint32_t)(falling & 0x01) << GPTMR_CTRL_FALL; + tmp |= (uint32_t)(filter & 0x01) << GPTMR_CTRL_FILTER; + tmp |= (uint32_t)(capture_irq & 0x01) << GPTMR_CTRL_IRQC; NEORV32_GPTMR->CTRL = tmp; } @@ -100,9 +120,71 @@ void neorv32_gptmr_enable(void) { /**********************************************************************//** - * Reset general purpose timer's counter register. + * Check if timer match has triggered. Clear trigger flag in that case. + * + * @return 1 if match trigger has fired, 0 if not. + **************************************************************************/ +int neorv32_gptmr_trigger_matched(void) { + + uint32_t tmp = NEORV32_GPTMR->CTRL; + + if (tmp & (1 << GPTMR_CTRL_TRIGM)) { + tmp &= ~((uint32_t)(1 << GPTMR_CTRL_TRIGM)); + NEORV32_GPTMR->CTRL = tmp; + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Check if capture input has triggered. Clear trigger flag in that case. + * + * @return 1 if capture trigger has fired, 0 if not. + **************************************************************************/ +int neorv32_gptmr_trigger_captured(void) { + + uint32_t tmp = NEORV32_GPTMR->CTRL; + + if (tmp & (1 << GPTMR_CTRL_TRIGC)) { + tmp &= ~((uint32_t)(1 << GPTMR_CTRL_TRIGC)); + NEORV32_GPTMR->CTRL = tmp; + return 1; + } + else { + return 0; + } +} + + +/**********************************************************************//** + * Reset general purpose timer's counter register (timer-mode only). **************************************************************************/ void neorv32_gptmr_restart(void) { NEORV32_GPTMR->COUNT = 0; } + + +/**********************************************************************//** + * Get current counter value. + * + * @return Current counter value. + **************************************************************************/ +uint32_t neorv32_gptmr_counter_get(void) { + + return NEORV32_GPTMR->COUNT; +} + + +/**********************************************************************//** + * Get latest capture value. + * + * @return Capture timer value. + **************************************************************************/ +uint32_t neorv32_gptmr_capture_get(void) { + + return NEORV32_GPTMR->CAPTURE; +} diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index df432ba0e..f130ff2fa 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -674,9 +674,39 @@ Clock prescaler select - GPTMR_CTRL_MODE + GPTMR_CTRL_IRQM [4:4] - Timer mode: 0=single-shot mode, 1=continuous mode + Enable interrupt on timer match + + + GPTMR_CTRL_IRQC + [5:5] + Enable interrupt on capture trigger + + + GPTMR_CTRL_RISE + [6:6] + Capture on rising edge; capture-mode only + + + GPTMR_CTRL_FALL + [7:7] + Capture on falling edge; capture-mode only + + + GPTMR_CTRL_FILTER + [8:8] + Filter capture input; capture-mode only + + + GPTMR_CTRL_TRIGM + [30:30] + Timer-match has fired, cleared by writing 0 + + + GPTMR_CTRL_TRIGC + [31:31] + Capture-trigger has fired, cleared by writing @@ -690,6 +720,12 @@ Counter register 0x08 + + CAPTURE + Capture register + 0x0C + read-only +