A 32-bit timer and PWM generator with the following features:
- 32-bit prescaler.
- Up Counting, Down Counting and Up/Down Counting.
- One-shot and Periodic.
- Two independent PWM channels with two compare registers.
- Optional PWM signal inversion.
- Configurable PWM dead time/band to generate PWM signals such as those required by a half-H bridge driver.
- Fault handling.
APB, AHBL, and Wishbone wrappers, generated by the BusWrap bus_wrap.py
utility, are provided. All wrappers provide the same programmer's interface as outlined in the following sections.
Based on your use case, use one of the provided wrappers or create a wrapper for your system bus type. For an example of how to integrate the APB wrapper:
EF_TMR32_APB INST (
`TB_APB_SLAVE_CONN,
.pwm0(pwm0),
.pwm1(pwm1),
.pwm_fault(pwm_fault)
);
NOTE: `TB_APB_SLAVE_CONN is a convenient macro provided by BusWrap.
The following table is the result for implementing the EF_TMR32 IP with different wrappers using Sky130 PDK and OpenLane2 flow.
Module | Number of cells | Max. freq |
---|---|---|
EF_TMR32 | 797 | 163 |
EF_TMR32_APB | 1435 | 135 |
EF_TMR32_AHBL | 1501 | 128 |
EF_TMR32_WB | 1669 | 63 |
Name | Offset | Reset Value | Access Mode | Description |
---|---|---|---|---|
TMR | 0000 | 0x00000000 | r | The current value of the Timer. |
RELOAD | 0004 | 0x00000000 | w | The timer reload value. In up counting it is used as the terminal count. For down counting it is used as the initial count. |
PR | 0008 | 0x00000000 | w | The Prescaler. The timer counting frequency is |
CMPX | 000c | 0x00000000 | w | Compare Register X. |
CMPY | 0010 | 0x00000000 | w | Compare Register Y. |
CTRL | 0014 | 0x00000000 | w | Control Register. |
CFG | 0018 | 0x00000000 | w | Configuration Register. |
PWM0CFG | 001c | 0x00000000 | w | PWM0 Configuration Register. |
PWM1CFG | 0020 | 0x00000000 | w | PWM1 Configuration Register. |
PWMDT | 0024 | 0x00000000 | w | PWM deadtime Register. |
PWMFC | 0028 | 0x00000000 | w | PWM fault clear register. |
IM | ff00 | 0x00000000 | w | Interrupt Mask Register; write 1/0 to enable/disable interrupts; check the interrupt flags table for more details |
RIS | ff08 | 0x00000000 | w | Raw Interrupt Status; reflects the current interrupts status;check the interrupt flags table for more details |
MIS | ff04 | 0x00000000 | w | Masked Interrupt Status; On a read, this register gives the current masked status value of the corresponding interrupt. A write has no effect; check the interrupt flags table for more details |
IC | ff0c | 0x00000000 | w | Interrupt Clear Register; On a write of 1, the corresponding interrupt (both raw interrupt and masked interrupt, if enabled) is cleared; check the interrupt flags table for more details |
The current value of the Timer.
The timer reload value. In up counting it is used as the terminal count. For down counting it is used as the initial count.
The Prescaler. The timer counting frequency is
bit | field name | width | description |
---|---|---|---|
0 | TE | 1 | Timer enable |
1 | TS | 1 | Timer re-start; used in the one-shot mode to restart the timer. Write 1 then 0 to re-start the timer. |
2 | P0E | 1 | PWM 0 enable |
3 | P1E | 1 | PWM 1 enable |
4 | DTE | 1 | PWM deadtime enable |
5 | PI0 | 1 | Invert PWM0 output. |
6 | PI1 | 1 | Invert PWM1 output. |
bit | field name | width | description |
---|---|---|---|
0 | DIR | 2 | Count direction; 10: Up, 01: Down, 11: Up/Down |
2 | P | 1 | 1: Periodic, 0: One Shot |
bit | field name | width | description |
---|---|---|---|
0 | E0 | 2 | PWM0 action for matching zero. 00: No Action, 01: Low, 10: High, 11: Invert |
2 | E1 | 2 | PWM0 action for matching CMPX (going up). 00: No Action, 01: Low, 10: High, 11: Invert |
4 | E2 | 2 | PWM0 action for matching CMPY (going up). 00: No Action, 01: Low, 10: High, 11: Invert |
6 | E3 | 2 | PWM0 action for matching RELOAD. 00: No Action, 01: Low, 10: High, 11: Invert |
8 | E4 | 2 | PWM0 action for matching CMPY (going down). 00: No Action, 01: Low, 10: High, 11: Invert |
10 | E5 | 2 | PWM0 action for matching CMPX (going down). 00: No Action, 01: Low, 10: High, 11: Invert |
bit | field name | width | description |
---|---|---|---|
0 | E0 | 2 | PWM1 action for matching zero. 00: No Action, 01: Low, 10: High, 11: Invert |
2 | E1 | 2 | PWM1 action for matching CMPX (going up). 00: No Action, 01: Low, 10: High, 11: Invert |
4 | E2 | 2 | PWM1 action for matching CMPY (going up). 00: No Action, 01: Low, 10: High, 11: Invert |
6 | E3 | 2 | PWM1 action for matching RELOAD. 00: No Action, 01: Low, 10: High, 11: Invert |
8 | E4 | 2 | PWM1 action for matching CMPY (going down). 00: No Action, 01: Low, 10: High, 11: Invert |
10 | E5 | 2 | PWM1 action for matching CMPX (going down). 00: No Action, 01: Low, 10: High, 11: Invert |
The wrapped IP provides four registers to deal with interrupts: IM, RIS, MIS and IC. These registers exist for all wrapper types generated by the BusWrap bus_wrap.py
utility.
Each register has a group of bits for the interrupt sources/flags.
-
IM
: is used to enable/disable interrupt sources. -
RIS
: has the current interrupt status (interrupt flags) whether they are enabled or disabled. -
MIS
: is the result of masking (ANDing) RIS by IM. -
IC
: is used to clear an interrupt flag.
The following are the bit definitions for the interrupt registers:
Bit | Flag | Width | Description |
---|---|---|---|
0 | TO | 1 | Timeout; TMR matches 0 (down counting) or RELOAD (up counting). |
1 | MX | 1 | TMR matches CMPX register. |
2 | MY | 1 | TMR matches CMPY register. |
Parameter | Description | Default Value |
---|---|---|
PRW | Number of bits for the prescaler register | 16 |
Port | Direction | Width | Description |
---|---|---|---|
pwm0 | output | 1 | The generated PWM0 signal |
pwm1 | output | 1 | The generated PWM1 signal |
pwm_fault | input | 1 | PWM fault input |
tmr_en | input | 1 | Flag to enable timer |
tmr_start | input | 1 | Flag to make tmr start in one shot mode |
pwm0_en | input | 1 | Enable signal for PWM0 generation |
pwm1_en | input | 1 | Enable signal for PWM1 generation |
tmr_reload | input | 32 | The reload value which the counter will reach or start from |
cmpx | input | 32 | The compare value X |
cmpy | input | 32 | The compare value Y |
prescaler | input | PRW | Prescaler value; Timer frequency = clock frequency / (prescaler + 1) |
tmr_cfg | input | 3 | Timer configuration value; periodic or one shot and counting direction |
pwm0_cfg | input | 12 | Actions configuration for pwm0 |
pwm1_cfg | input | 12 | Actions configuration for pwm1 |
pwm0_inv | input | 1 | Invert pwm0 signal |
pwm1_inv | input | 1 | Invert pwm1 signal |
pwm_dt | input | 8 | Deadtime for pwm |
pwm_fault_clr | input | 16 | PWM fault input |
pwm_dt_en | input | 1 | PWM deadtime enable |
tmr | output | 32 | The actual value for the timer |
matchx_flag | output | 1 | Flag raised when matching compare value x |
matchy_flag | output | 1 | Flag raised when matching compare value x |
timeout_flag | output | 1 | Flag raised when timeout happen |
- To use the timer only:
- Set the value of reload (the maximum value the timer counter will reach if up counting or the value it will start from when down counting) by writing to
RELOAD
register - Set the timer counting frequency by writing to the
PR
register where the timer counting frequency is$Clock\ freq/(PR + 1)$ | - Choose whether you want the timer counter to be up counting, down counting, up/down counting by setting the
DIR
field inCFG
register - Choose whether you want the timer counter to be periodic (starts counting again after reaching reload value) or one shot (count only one time and stays at reload value) by setting the
P
field inCFG
register - Enable the timer by setting
TE
field inCTRL
register , if one shot mode is used, useTS
field to restart the counter - Get the actual timer value through reading
TMR
register
- To generate pwm signals:
- Do the exact same steps (from 1 to 5) for using the timer
- Set the values for the two compare registers, if needed, by writing to
CMPX
andCMPY
registers - Choose the actions you want when the timer reaches each of zero, cmpx (up counting), cmpy (up counting), reload, cmpx (down counting), cmpy (down counting). The actions could be either no action, high, low, or invert. You can set the actions by writing to
PWM0CFG
if using pwm0 orPWM1CFG
if using pwm1. - Enable the timer, and enable pwm0 and/or pwm1 by writing to
CTRL
register
You can either clone repo or use IPM which is an open-source IPs Package Manager
- To clone repo:
git clone https://github.com/efabless/EF_TMR32/tree/main
- To download via IPM , follow installation guides here then run
ipm install EF_TMR32
- Clone IP_Utilities repo in the same directory as the IP
- In the directory
EF_TMR32/verify/utb/
runmake APB-RTL
to run testbench for APB ormake AHBL-RTL
to run testbench for AHBL
In IP directory run:
cd verify/uvm-python/
To run all tests:
make run_all_tests BUS_TYPE=APB
To run a certain test:
make run_<test_name> BUS_TYPE=APB
To run all tests with a tag:
make run_all_tests TAG=<new_tag> BUS_TYPE=APB
To run all tests:
make run_all_tests BUS_TYPE=AHB
To run a certain test:
make run_<test_name> BUS_TYPE=AHB
To run all tests with a tag:
make run_all_tests TAG=<new_tag> BUS_TYPE=AHB