diff --git a/CHANGELOG.md b/CHANGELOG.md index 11fa53dcf..6da21f9d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12 | Date | Version | Comment | Ticket | |:----:|:-------:|:--------|:------:| +| 04.07.2024 | 1.10.0.10 | :warning: rework GPTMRM and remove capture mode | [#939](https://github.com/stnolting/neorv32/pull/939) | | 03.07.2024 | 1.10.0.9 | :warning: remove `AMO_RVS_GRANULARITY` generic, reservation set granularity is now fixed to 4 bytes | [#938](https://github.com/stnolting/neorv32/pull/938) | | 03.07.2024 | 1.10.0.8 | :test_tube: add XBUS to AHB3-lite bridge | [#937](https://github.com/stnolting/neorv32/pull/937) | | 02.07.2024 | 1.10.0.7 | minor rtl and software edits and cleanups | [#936](https://github.com/stnolting/neorv32/pull/936) | diff --git a/README.md b/README.md index 9ecfea861..477171336 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,6 @@ For example, the CPU ensures _all_ memory accesses are properly acknowledged and instructions are always detected as such. Whenever an unexpected state occurs the application software is informed via _precise_ and resumable hardware exceptions. -* :books: For detailed information see the [NEORV32 online documentation](https://stnolting.github.io/neorv32/). * :recycle: Looking for an **all-Verilog** version? Have a look at [neorv32-verilog](https://github.com/stnolting/neorv32-verilog). * :heavy_check_mark: [Continuous integration](#project-status) to check for regressions (including RISC-V ISA compatibility check using **RISCOF**). * :open_file_folder: [Exemplary setups](https://github.com/stnolting/neorv32-setups) and @@ -46,6 +45,7 @@ targeting various FPGA boards and toolchains to get started. * :label: The project's change log is available in [CHANGELOG.md](https://github.com/stnolting/neorv32/blob/main/CHANGELOG.md). * :rocket: Check out the [quick links below](#5-getting-started) and the [*User Guide*](https://stnolting.github.io/neorv32/ug/) to get started setting up _your_ NEORV32 processor! +* :books: For detailed information see the [NEORV32 online documentation](https://stnolting.github.io/neorv32/). * :interrobang: Want to know more? Check out the [project's rationale](https://stnolting.github.io/neorv32/#_rationale). Feel free to open a new [issue](https://github.com/stnolting/neorv32/issues) or start a new @@ -140,7 +140,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)) with capture input +* 32-bit general purpose timer ([GPTMR](https://stnolting.github.io/neorv32/#_general_purpose_timer_gptmr)) * 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 01a039d47..e3a7ed6c9 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**>>) with capture input +* _optional_ general purpose 32-bit timer (<<_general_purpose_timer_gptmr,**GPTMR**>>) * _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**>>) @@ -151,8 +151,6 @@ Some interfaces (like the TWI and the 1-Wire bus) require tri-state drivers in t | `neoled_o` | 1 | out | - | asynchronous serial data output 5+^| **<<_machine_system_timer_mtime>>** | `mtime_time_o` | 64 | out | - | MTIME system time output -5+^| **<<_general_purpose_timer_gptmr>>** -| `gptmr_trig_i` | 1 | in | `'L'` | timer capture input 5+^| **<<_external_interrupt_controller_xirq>>** | `xirq_i` | 32 | in | `'L'` | external interrupt requests 5+^| **RISC-V Machine-Mode <<_processor_interrupts>>** diff --git a/docs/datasheet/soc_gptmr.adoc b/docs/datasheet/soc_gptmr.adoc index 48aead7dc..4663c3cc4 100644 --- a/docs/datasheet/soc_gptmr.adoc +++ b/docs/datasheet/soc_gptmr.adoc @@ -8,7 +8,7 @@ | Hardware source files: | neorv32_gptmr.vhd | | Software driver files: | neorv32_gptmr.c | | | neorv32_gptmr.h | -| Top entity ports: | `gptmr_trig_i` | timer capture input +| Top entity ports: | none | | Configuration generics: | `IO_GPTMR_EN` | implement general purpose timer when `true` | CPU interrupts: | fast IRQ channel 12 | timer interrupt (see <<_processor_interrupts>>) |======================= @@ -18,14 +18,13 @@ 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. +when reaching a programmable threshold value.l. -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: +The GPTMR provides three interface registers : a control register (`CTRL`), a 32-bit counter register (`COUNT`) and a +32-bit threshold register (`THRES`). The timer is globally enabled by setting the `GPTMR_CTRL_EN` bit in the module's +control register. When the timer is enable the `COUNT` register will start incrementing from zero at a programmable +rate that scales the main processor clock. this pre-scaler is configured via the three `GPTMR_CTRL_PRSCx` +control register bits: .GPTMR prescaler configuration [cols="<4,^1,^1,^1,^1,^1,^1,^1,^1"] @@ -35,40 +34,24 @@ three `GPTMR_CTRL_PRSCx` control register bits: | Resulting `clock_prescaler` | 2 | 4 | 8 | 64 | 128 | 1024 | 2048 | 4096 |======================= -[NOTE] -Disabling the timer will not clear the `COUNT` register. However, it can be manually reset at any time by -writing zero to it. - - -**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. +Whenever the counter register `COUNT` equals the programmable threshold value `THRES` the module's interrupt +signal becomes pending (indicated by `GPTMR_CTRL_IRQ_PND` being set). Note that a pending interrupt has to be +cleared manually by writing a `1` to `GPTMR_CTRL_IRQ_CLR`. +The control register's `GPTMR_CTRL_MODE` bit defines what will happen when `COUNT == THRES`. -**Timer Capture** +* `GPTMR_CTRL_MODE = 0`: **single-shot mode** - the `COUNT` register will stop incrementing +* `GPTMR_CTRL_MODE = 1`: **continuous mode** - the `COUNT` register is automatically reset and restarts incrementing from zero -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 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. +.Resetting the Counter +[NOTE] +Disabling the GPTMR will also clear the `COUNT` register. **Interrupt** -The GPTRM provides a single interrupt line that can be trigger by a timer-match event and/or by a timer-compare -event. Once triggered, the interrupt will stay active until explicitly cleared by writing zero to the according -interrupt flag (`GPTMR_CTRL_TRIGM` or `GPTMR_CTRL_TRIGC`). +The GPTRM provides a single interrupt line is triggered whenever `COUNT` equals `THRES`. Once triggered, the interrupt will +stay pending until explicitly cleared by writing a 1 to `GPTMR_CTRL_IRQ_CLR`. **Register Map** @@ -78,17 +61,12 @@ interrupt flag (`GPTMR_CTRL_TRIGM` or `GPTMR_CTRL_TRIGC`). [options="header",grid="all"] |======================= | Address | Name [C] | Bit(s), Name [C] | R/W | Function -.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 +.6+<| `0xfffff100` .6+<| `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 <| Operation mode (0=single-shot, 1=continuous) + <|`29:5` - ^| r/- <| _reserved_, read as zero + <|`30` `GPTMR_CTRL_IRQ_CLR` ^| -/w <| Write `1` to clear timer-match interrupt; auto-clears + <|`31` `GPTMR_CTRL_IRQ_PND` ^| r/- <| Timer-match interrupt pending | `0xfffff104` | `THRES` |`31:0` | r/w | Threshold value register -| `0xfffff108` | `COUNT` |`31:0` | r/w | Counter register -| `0xfffff10C` | `CAPTURE` |`31:0` | r/- | Capture register +| `0xfffff108` | `COUNT` |`31:0` | r/- | Counter register |======================= diff --git a/rtl/core/neorv32_gptmr.vhd b/rtl/core/neorv32_gptmr.vhd index 37a7af380..f6680dd0e 100644 --- a/rtl/core/neorv32_gptmr.vhd +++ b/rtl/core/neorv32_gptmr.vhd @@ -1,8 +1,6 @@ -- ================================================================================ -- -- NEORV32 SoC - General Purpose Timer (GPTMR) -- -- -------------------------------------------------------------------------------- -- --- Can operate in interval timer mode and/or in input capture mode. -- --- -------------------------------------------------------------------------------- -- -- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 -- -- Copyright (c) NEORV32 contributors. -- -- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. -- @@ -25,55 +23,35 @@ 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 - capture_i : in std_ulogic -- capture input + irq_o : out std_ulogic -- timer match interrupt ); end neorv32_gptmr; architecture neorv32_gptmr_rtl of neorv32_gptmr is -- control register -- - constant ctrl_en_c : natural := 0; -- r/w: global 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 + constant ctrl_en_c : natural := 0; -- r/w: global 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_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 + constant ctrl_irq_clr_c : natural := 30; -- -/w: set to clear timer-match interrupt + constant ctrl_irq_c : natural := 31; -- r/-: timer-match interrupt pending -- - signal ctrl : std_ulogic_vector(8 downto 0); - - -- trigger flags -- - signal trig_match, trig_capture : std_ulogic; + signal ctrl : std_ulogic_vector(4 downto 0); -- timer core -- type timer_t is record - 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 + tick : std_ulogic; -- clock generator tick + thres : std_ulogic_vector(31 downto 0); -- threshold register + count : std_ulogic_vector(31 downto 0); -- counter register + match : std_ulogic; -- counter register == threshold 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; + -- interrupt generator -- + signal irq_pnd, irq_clr : std_ulogic; begin @@ -82,167 +60,103 @@ begin bus_access: process(rstn_i, clk_i) begin if (rstn_i = '0') then - bus_rsp_o <= rsp_terminate_c; - ctrl <= (others => '0'); - trig_match <= '0'; - trig_capture <= '0'; - timer.cnt_we <= '0'; - timer.thres <= (others => '0'); + bus_rsp_o <= rsp_terminate_c; + ctrl <= (others => '0'); + timer.thres <= (others => '0'); + irq_clr <= '0'; elsif rising_edge(clk_i) then -- defaults -- bus_rsp_o.ack <= bus_req_i.stb; bus_rsp_o.err <= '0'; -- no access error possible bus_rsp_o.data <= (others => '0'); - timer.cnt_we <= '0'; - - -- IRQ trigger -- - trig_match <= ctrl(ctrl_en_c) and ctrl(ctrl_irqm_c) and (trig_match or timer.trigger); - trig_capture <= ctrl(ctrl_en_c) and ctrl(ctrl_irqc_c) and (trig_capture or capture.trigger); - + irq_clr <= '0'; -- actual bus access -- if (bus_req_i.stb = '1') then if (bus_req_i.rw = '1') then -- write access 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_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; + 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); + -- + irq_clr <= bus_req_i.data(ctrl_irq_clr_c); end if; if (bus_req_i.addr(3 downto 2) = "01") then -- threshold register timer.thres <= bus_req_i.data; end if; - if (bus_req_i.addr(3 downto 2) = "10") then -- counter register - timer.cnt_we <= '1'; - end if; else -- read access 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_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_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_trigm_c) <= trig_match; - bus_rsp_o.data(ctrl_trigc_c) <= trig_capture; + bus_rsp_o.data(ctrl_irq_c) <= irq_pnd; when "01" => -- threshold register bus_rsp_o.data <= timer.thres; - when "10" => -- counter register + when others => -- counter register bus_rsp_o.data <= timer.count; - when others => -- capture register - bus_rsp_o.data <= capture.count; end case; end if; end if; end if; end process bus_access; - -- interrupt request -- - irq_o <= trig_match or trig_capture; + -- clock generator enable -- + clkgen_en_o <= ctrl(ctrl_en_c); -- Timer Core ----------------------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - counter_core: process(rstn_i, clk_i) + timer_core: process(rstn_i, clk_i) begin if (rstn_i = '0') then - timer.count <= (others => '0'); - timer.match_ff <= '0'; + timer.tick <= '0'; + timer.count <= (others => '0'); elsif rising_edge(clk_i) then - if (timer.cnt_we = '1') then -- write access - 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 -- timer-match - timer.count <= (others => '0'); + timer.tick <= clkgen_i(to_integer(unsigned(ctrl(ctrl_prsc2_c downto ctrl_prsc0_c)))); -- prescaler clock tick + if (ctrl(ctrl_en_c) = '0') then -- timer disabled + timer.count <= (others => '0'); + elsif (timer.tick = '1') then -- timer 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; 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; + end process timer_core; -- counter = threshold? -- - timer.match <= '1' when (timer.count = timer.thres) and (ctrl(ctrl_en_c) = '1') else '0'; + timer.match <= '1' when (timer.count = timer.thres) else '0'; - -- 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) - begin - if (rstn_i = '0') then - timer.tick <= '0'; - elsif rising_edge(clk_i) then - timer.tick <= clkgen_i(to_integer(unsigned(ctrl(ctrl_prsc2_c downto ctrl_prsc0_c)))); - end if; - end process clock_select; - - -- clock generator enable -- - clkgen_en_o <= ctrl(ctrl_en_c); - - - -- Capture Control ------------------------------------------------------------------------ + -- Interrupt Generator -------------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - signal_capture: process(rstn_i, clk_i) + irq_generator: 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'); + irq_pnd <= '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); + if (ctrl(ctrl_en_c) = '1') then + if (timer.match = '1') then + irq_pnd <= '1'; + elsif (irq_clr = '1') then + irq_pnd <= '0'; end if; - end if; - - -- timer capture -- - if (ctrl(ctrl_en_c) = '1') and (capture.trigger = '1') then - capture.count <= timer.count; + else + irq_pnd <= '0'; 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'; + end process irq_generator; - -- 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 request -- + irq_o <= irq_pnd; end neorv32_gptmr_rtl; diff --git a/rtl/core/neorv32_package.vhd b/rtl/core/neorv32_package.vhd index a954295aa..c67b3f84a 100644 --- a/rtl/core/neorv32_package.vhd +++ b/rtl/core/neorv32_package.vhd @@ -29,7 +29,7 @@ package neorv32_package is -- Architecture Constants ----------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- - constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100009"; -- hardware version + constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100010"; -- hardware version constant archid_c : natural := 19; -- official RISC-V architecture ID constant XLEN : natural := 32; -- native data path width @@ -879,8 +879,6 @@ package neorv32_package is neoled_o : out std_ulogic; -- Machine timer system time (available if IO_MTIME_EN = true) -- mtime_time_o : out std_ulogic_vector(63 downto 0); - -- 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 43b0244d5..64d47a177 100644 --- a/rtl/core/neorv32_top.vhd +++ b/rtl/core/neorv32_top.vhd @@ -222,9 +222,6 @@ entity neorv32_top is -- Machine timer system time (available if IO_MTIME_EN = true) -- mtime_time_o : out std_ulogic_vector(63 downto 0); -- current system time - -- 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 @@ -1485,8 +1482,7 @@ begin bus_rsp_o => iodev_rsp(IODEV_GPTMR), clkgen_en_o => cg_en(CG_GPTMR), clkgen_i => clk_gen, - irq_o => firq(FIRQ_GPTMR), - capture_i => gptmr_trig_i + irq_o => firq(FIRQ_GPTMR) ); end generate; diff --git a/rtl/system_integration/neorv32_vivado_ip.tcl b/rtl/system_integration/neorv32_vivado_ip.tcl index f145ea588..7a154950b 100644 --- a/rtl/system_integration/neorv32_vivado_ip.tcl +++ b/rtl/system_integration/neorv32_vivado_ip.tcl @@ -86,7 +86,6 @@ set_property enablement_dependency {$io_pwm_num_ch > 0} [ipx::get_ports set_property enablement_dependency {$io_cfs_en = true} [ipx::get_ports cfs_* -of_objects [ipx::current_core]] set_property enablement_dependency {$io_neoled_en = true} [ipx::get_ports neoled_o -of_objects [ipx::current_core]] set_property enablement_dependency {$io_mtime_en = true} [ipx::get_ports mtime_time_o -of_objects [ipx::current_core]] -set_property enablement_dependency {$io_gptmr_en = true} [ipx::get_ports gptmr_trig_i -of_objects [ipx::current_core]] set_property enablement_dependency {$xirq_num_ch > 0} [ipx::get_ports xirq_i -of_objects [ipx::current_core]] set_property enablement_dependency {$io_mtime_en = false} [ipx::get_ports mtime_irq_i -of_objects [ipx::current_core]] diff --git a/rtl/system_integration/neorv32_vivado_ip.vhd b/rtl/system_integration/neorv32_vivado_ip.vhd index c7559571e..793e99da3 100644 --- a/rtl/system_integration/neorv32_vivado_ip.vhd +++ b/rtl/system_integration/neorv32_vivado_ip.vhd @@ -226,8 +226,6 @@ entity neorv32_vivado_ip is neoled_o : out std_ulogic; -- Machine timer system time (available if IO_MTIME_EN = true) -- mtime_time_o : out std_ulogic_vector(63 downto 0); - -- GPTMR timer capture (available if IO_GPTMR_EN = true) -- - gptmr_trig_i : in std_ulogic := '0'; -- External platform interrupts (available if XIRQ_NUM_CH > 0) -- xirq_i : in std_ulogic_vector(XIRQ_NUM_CH-1 downto 0) := (others => '0'); -- CPU Interrupts -- @@ -437,8 +435,6 @@ begin neoled_o => neoled_o, -- Machine timer system time (available if IO_MTIME_EN = true) -- mtime_time_o => mtime_time_o, - -- GPTMR timer capture (available if IO_GPTMR_EN = true) -- - gptmr_trig_i => gptmr_trig_i, -- External platform interrupts (available if XIRQ_NUM_CH > 0) -- xirq_i => xirq_i_aux, -- CPU Interrupts -- diff --git a/sim/neorv32_tb.vhd b/sim/neorv32_tb.vhd index 1d98bc3f8..1fc680a09 100644 --- a/sim/neorv32_tb.vhd +++ b/sim/neorv32_tb.vhd @@ -357,8 +357,6 @@ begin neoled_o => open, -- async serial data line -- Machine timer system time (available if IO_MTIME_EN = true) -- mtime_time_o => open, - -- 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 7a2f2a07e..a15df093b 100644 --- a/sim/simple/neorv32_tb.simple.vhd +++ b/sim/simple/neorv32_tb.simple.vhd @@ -333,8 +333,6 @@ begin neoled_o => open, -- async serial data line -- Machine timer system time (available if IO_MTIME_EN = true) -- mtime_time_o => open, - -- 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 ad59b6f07..2e14bc7d2 100644 --- a/sw/example/demo_dma/main.c +++ b/sw/example/demo_dma/main.c @@ -238,7 +238,7 @@ int main() { // configure GPTMR neorv32_gptmr_setup(CLK_PRSC_2, // GPTM clock = 1/2 main clock 4096, // counter threshold for triggering IRQ - 1); // enable timer-match interrupt + 0); // single-shot mode // configure transfer type cmd = DMA_CMD_B2B | // read source in byte quantities, write destination in byte quantities @@ -270,6 +270,7 @@ int main() { neorv32_uart0_printf("Transfer succeeded!\n"); } + neorv32_gptmr_disable(); // disable GPTMR show_arrays(); } else { @@ -338,7 +339,7 @@ void show_arrays(void) { **************************************************************************/ void dma_firq_handler(void) { - neorv32_gptmr_trigger_matched(); // clear GPTMR timer-match interrupt + neorv32_gptmr_irq_ack(); // clear GPTMR timer-match interrupt NEORV32_DMA->CTRL &= ~(1<>\n"); diff --git a/sw/example/demo_gptmr/main.c b/sw/example/demo_gptmr/main.c index c1c5d0d32..69332f147 100644 --- a/sw/example/demo_gptmr/main.c +++ b/sw/example/demo_gptmr/main.c @@ -34,7 +34,7 @@ void gptmr_firq_handler(void); * * @note This program requires the GPTMR unit to be synthesized (and UART0 and GPIO). * - * @return Should not return; + * @return Should not return. **************************************************************************/ int main() { @@ -63,7 +63,7 @@ int main() { // install GPTMR interrupt handler neorv32_rte_handler_install(GPTMR_RTE_ID, gptmr_firq_handler); - // configure timer for 0.5Hz ticks with clock divisor = 8 and enable timer-match interrupt + // configure timer for 0.5Hz ticks with clock divisor = 8 and set to run in continuous mode neorv32_gptmr_setup(CLK_PRSC_8, NEORV32_SYSINFO->CLK / (8 * 2), 1); // enable interrupt @@ -87,7 +87,7 @@ int main() { **************************************************************************/ void gptmr_firq_handler(void) { - neorv32_gptmr_trigger_matched(); // clear timer-match interrupt + neorv32_gptmr_irq_ack(); // clear pending timer-internal interrupt neorv32_uart0_putc('.'); // send tick symbol via UART0 neorv32_gpio_pin_toggle(0); // toggle output port bit 0 diff --git a/sw/example/processor_check/main.c b/sw/example/processor_check/main.c index 829840cef..da139526d 100644 --- a/sw/example/processor_check/main.c +++ b/sw/example/processor_check/main.c @@ -1494,8 +1494,8 @@ int main() { // enable GPTMR FIRQ neorv32_cpu_csr_write(CSR_MIE, 1 << GPTMR_FIRQ_ENABLE); - // match-interrupt after CLK_PRSC_2*THRESHOLD = 2*2 = 8 clock cycles - neorv32_gptmr_setup(CLK_PRSC_2, 2, 1); + // match-interrupt after CLK_PRSC_2*THRESHOLD = 2*2 = 8 clock cycles, single-shot mode + neorv32_gptmr_setup(CLK_PRSC_2, 2, 0); // wait for interrupt asm volatile ("nop"); @@ -1504,8 +1504,7 @@ int main() { neorv32_cpu_csr_write(CSR_MIE, 0); 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? + (NEORV32_GPTMR->CTRL & (1 << GPTMR_CTRL_IRQ_PND))) { // timer interrupt pending? test_ok(); } else { diff --git a/sw/lib/include/neorv32_gptmr.h b/sw/lib/include/neorv32_gptmr.h index bd8a355c6..2450c60d5 100644 --- a/sw/lib/include/neorv32_gptmr.h +++ b/sw/lib/include/neorv32_gptmr.h @@ -27,10 +27,9 @@ /**@{*/ /** 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 */ - uint32_t CAPTURE; /**< offset 12: capture register */ + uint32_t CTRL; /**< offset 0: control register (#NEORV32_GPTMR_CTRL_enum) */ + uint32_t THRES; /**< offset 4: threshold register */ + const uint32_t COUNT; /**< offset 8: counter register, read-only */ } neorv32_gptmr_t; /** GPTMR module hardware access (#neorv32_gptmr_t) */ @@ -42,14 +41,10 @@ enum NEORV32_GPTMR_CTRL_enum { 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_MODE = 4, /**< GPTMR control register(4) (r/w): Operation mode (0=single-shot, 1=continuous) */ - 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 */ + GPTMR_CTRL_IRQ_CLR = 30, /**< GPTMR control register(30) (-/w): Set to clear timer-match interrupt */ + GPTMR_CTRL_IRQ_PND = 31, /**< GPTMR control register(31) (r/-): Timer-match interrupt pending */ }; /**@}*/ @@ -58,16 +53,11 @@ enum NEORV32_GPTMR_CTRL_enum { * @name Prototypes **************************************************************************/ /**@{*/ -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); +int neorv32_gptmr_available(void); +void neorv32_gptmr_setup(int prsc, uint32_t threshold, int cont_mode); +void neorv32_gptmr_disable(void); +void neorv32_gptmr_enable(void); +void neorv32_gptmr_irq_ack(void); /**@}*/ diff --git a/sw/lib/source/neorv32_gptmr.c b/sw/lib/source/neorv32_gptmr.c index 1d485cbb3..9015f3345 100644 --- a/sw/lib/source/neorv32_gptmr.c +++ b/sw/lib/source/neorv32_gptmr.c @@ -40,38 +40,17 @@ int neorv32_gptmr_available(void) { * * @param[in] prsc Clock prescaler select (0..7). See #NEORV32_CLOCK_PRSC_enum. * @param[in] threshold Threshold value, counter will reset to zero when reaching this. - * @param[in] match_irq Fire interrupt when counter matches threshold value. + * @param[in] cont_mode Set to operate timer in continuous mode (instead of single-shot mode). **************************************************************************/ -void neorv32_gptmr_setup(int prsc, uint32_t threshold, int match_irq) { +void neorv32_gptmr_setup(int prsc, uint32_t threshold, int cont_mode) { - NEORV32_GPTMR->CTRL = 0; // reset configuration + NEORV32_GPTMR->CTRL = 0; // reset module 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)(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; + tmp |= (uint32_t)(cont_mode & 0x01) << GPTMR_CTRL_MODE; NEORV32_GPTMR->CTRL = tmp; } @@ -95,71 +74,9 @@ void neorv32_gptmr_enable(void) { /**********************************************************************//** - * 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. + * Clear pending timer interrupt. **************************************************************************/ -uint32_t neorv32_gptmr_capture_get(void) { +void neorv32_gptmr_irq_ack(void) { - return NEORV32_GPTMR->CAPTURE; + NEORV32_GPTMR->CTRL |= ((uint32_t)(1 << GPTMR_CTRL_IRQ_CLR)); } diff --git a/sw/svd/neorv32.svd b/sw/svd/neorv32.svd index 7b7a2bf49..16e5ea1c0 100644 --- a/sw/svd/neorv32.svd +++ b/sw/svd/neorv32.svd @@ -712,39 +712,20 @@ Clock prescaler select - GPTMR_CTRL_IRQM + GPTMR_CTRL_MODE [4:4] - Enable interrupt on timer match + Operation mode (0=single-shot, 1=continuous) - 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 + GPTMR_CTRL_IRQ_CLR [30:30] - Timer-match has fired, cleared by writing 0 + Set to clear timer-match interrupt - GPTMR_CTRL_TRIGC + GPTMR_CTRL_IRQ_PND [31:31] - Capture-trigger has fired, cleared by writing + read-only + Timer-match interrupt pending @@ -757,11 +738,6 @@ COUNT Counter register 0x08 - - - CAPTURE - Capture register - 0x0C read-only