I'm working on a bare-metal interrupt controller, GIC version 3. The underlying architecture is Virt, with QEMU, and a CPU Arm Cortex-72, aarch64:
qemu-system-aarch64 -machine virt,gic-version=3 -cpu cortex-a72 -nographic -kernel kernel.elf
According to the ARMv8-A guide, Interrupt Group 1 Enable register, bit 0 of register ICC_IGRPEN1_EL1 is the responsible for IRQs activation. This is an excerpt of it:
I specify that I work in NON SECURE mode.
I want to generate an IRQ from a timing context. The timer works well, but for any reason the IRQ is not captured even though bit 0of ICC_IGRPEN1_EL1 is set to 1.
Since I use several headers, I can't provide you a minimum example. I have no way, if you can suggest me how, please do it. However, I did the best possible to formulate a clear question, so I really hope you can help.
This is an excerpt of the function which shall generate the IRQ:
void timer_init(void)
{
uint64_t ticks, current_cnt;
// Disable the timer
disable_cntv();
//read system frequency
cntfrq = raw_read_cntfrq_el0();
// Next timer IRQ is after 3 sec(s).
ticks = 3 * cntfrq;
// Get value of the current timer
current_cnt = raw_read_cntvct_el0();
// Set the interrupt in Current Time + TimerTick
raw_write_cntv_cval_el0(current_cnt + ticks);
// Enable the timer
enable_cntv();
// Enable IRQ
enable_irqs();
while(1){
wfi();
}
}
Function enable_irqs has been defined as follows:
void enable_irqs(void) {
set_gic_gicc_igrpen1_el1(0x1);
}
In turn, function set_gic_gicc_igrpen1_el1 is:
void set_gic_gicc_igrpen1_el1(uint64_t value)
{
__asm__ __volatile__("msr s3_0_c12_c12_7 , %0" : : "r" (value));
}
I verified all the values with gdb: timer works and register ICC_IGRPEN1_EL1 has the last bit which is set to 1. Do you notice any errors in this code for which I do not receive any IRQs?
Not to be incomplete, the other functions I mentioned are implemented as follows:
void disable_cntv(void)
{
uint32_t cntv_ctl;
cntv_ctl = raw_read_cntv_ctl();
cntv_ctl &= ~CNTV_CTL_ENABLE;
__asm__ __volatile__("msr CNTV_CTL_EL0, %0\n\t" : : "r" (cntv_ctl) : "memory");
}
void enable_cntv(void)
{
uint32_t cntv_ctl;
cntv_ctl = raw_read_cntv_ctl();
cntv_ctl |= CNTV_CTL_ENABLE;
__asm__ __volatile__("msr CNTV_CTL_EL0, %0\n\t" : : "r" (cntv_ctl) : "memory");
}
uint32_t raw_read_cntfrq_el0(void)
{
uint32_t cntfrq_el0;
__asm__ __volatile__("mrs %0, CNTFRQ_EL0\n\t" : "=r" (cntfrq_el0) : : "memory");
return cntfrq_el0;
}
void raw_write_cntv_cval_el0(uint64_t cntv_cval_el0)
{
__asm__ __volatile__("msr CNTV_CVAL_EL0, %0\n\t" : : "r" (cntv_cval_el0) : "memory");
}
uint64_t raw_read_cntvct_el0(void)
{
uint64_t cntvct_el0;
__asm__ __volatile__("mrs %0, CNTVCT_EL0\n\t" : "=r" (cntvct_el0) : : "memory");
return cntvct_el0;
}
/* Wait For Interrupt */
#define wfi() asm volatile("wfi" : : : "memory")
CNTV_CTL_ENABLE is defined as follows:
#define CNTV_CTL_ENABLE (1 << 0) /* Enables the timer */
Related
I am a newbie to programming in the Microchip PIC environment, so please excuse my naivete! I recently began experimenting with the dsPIC33CK Curiosity dev board (which contains the dsPIC33CK256MP508 dsp/mcu at its core), and have been exploring the on-board modules, like direct memory access (DMA) and the peripheral trigger generator (PTG).
DMA is working, but I am having trouble with a seemingly-simple PTG task, and I was wondering if anyone has any ideas -- the task involves a simple external interrupt/trigger to the PTG, which then needs to trigger another external pin to go logic-high.
In real life, an incoming logic-high signal will indicate the start of a series of analog data to be read, so the outgoing signal will (repeatedly) trigger an external ADC. I am using an external ADC (an ADS831, which I have working well) since the internal ADC in the dsPIC33 does not sample at the rates I need.
I am unsure if my mistake lies with my PTG commands, or if maybe my Peripheral Pin Select (PPS) module setup is flawed. Unfortunately, it appears that the builtin IO pin unlocking function ("__builtin_write_RPCON()") is undefined, at least with my included libraries. So, fell back on in-line assembler to perform the unlock sequence (detailed in datasheet DS70005349J-page 132).
Also strange: you'll notice I start my PTG sequence on "STEP3", though the PTG documentation (e.g., DS70000669B-page 33) advises starting at "STEP0". The reason I've done this is because "STEP1" and "STEP2" are apparently used by more than one Special Function Register (SFR). I discovered this in a support file included in my compiler directory (specifically, lines 26278 and 26281 of file "p33CK256MP508.h").
Anyway, here is my simple PTG-testbed code:
/* Testbed to explore the PTG module.
*
* File: newmain.c
* Author: benjamin sadler
*
* Created on January 26, 2023, 11:43 AM
*/
#include <xc.h>
/*
#define PTGCTRL(x) ((0x0 << 4) | ((x) & 0x0F)) // PTG command definitions
#define PTGWHI(x) ((0x4 << 4) | ((x) & 0x0F)) // from DS70000669B-page 15
#define PTGTRIG(x) ((0x4 << 5) | ((x) & 0x1F)) // (currently commented to
#define PTGJMPC0(x) ((0x6 << 5) | ((x) & 0x1F)) // try alternate commands
*/ // (see lines 58-60))
void init_PTG(void); // init function primitives
void init_PPS(void);
int main(int argc, char** argv) {
ANSELB = 0x0; // configure PORTB and PORTC
ANSELC = 0x0; // as digital, and set
TRISBbits.TRISB15 = 0; // direction bits:
TRISCbits.TRISC0 = 1; // B15->out, C0->in
init_PPS(); // initialize PPS
init_PTG(); // initialize PTG
PTGCSTbits.PTGSTRT = 1; // start PTG ...
while(1); // .. and wait for INT2->high.
return (1);
}
void init_PPS( void )
{
INTCON2bits.GIE = 1; // enable global interrupts
asm ("mov #0x55, w0"); // "__builtin_write_RPCON()"
asm ("mov w0, _NVMKEY"); // function doesn't appear
asm ("mov #0xAA, w0"); // to be defined, so in-line
asm ("mov w0, _NVMKEY"); // assembly needed unlock IOLOCK
RPCONbits.IOLOCK = 0; // (that is, set IOLOCK = 0)
_INT2R = 48; // PPS connect INT2 with RP48/PORTC0
asm ("mov #0x55, w0"); // more in-line assembly to
asm ("mov w0, _NVMKEY"); // re-lock IOLOCK ...
asm ("mov #0xAA, w0");
asm ("mov w0, _NVMKEY");
RPCONbits.IOLOCK = 1; // re-lock IOLOCK
}
void init_PTG( void )
{
PTGCON = 0; // most bits on PTGCST and
PTGCST = 0; // PTGCST registers should be
PTGCSTbits.PTGEN = 1; // zero, except PTGEN=1 which
// enables module
PTGQPTR = 3; // start at STEP3 (STEP1, STEP2
// aren't available??)
PTGC0LIM = 5; // loop 5 times back to STEP3
/* // set STEP commands:
_STEP3 = PTGWHI(15); // wait for INT2 interrupt
_STEP4 = PTGTRIG(25); // then trigger RP47/B14 high
_STEP5 = PTGJMPC0(3); // and jump back to STEP3
*/
_STEP3 = 0b01001111; // alternate try at setting
_STEP4 = 0b10011001; // PTG step commands, using
_STEP5 = 0b11000011; // data from Table 24-1 in
} // datasheet DS70005349J-page 478
And here are the results I'm getting:
Using the simulator (MPLAB X IDE v6.05, with xc16 compiler v2.00) with the correct device (dsPIC33CK256MP508) selected, this code will compile and run, but when I feed in a simulated RC0-> high with the Stimulus tool, I can see that PORTC0 goes high in the variable watch window, but the PTG-triggered output (on RP47/PORTB15) is never observed.
B15 stays low :(
You can also see I've tried programming the PTG steps both using the suggested bit-wise operators (currently commented out), as well as manually loading the bit fields.
I have worked on this for several days without any success, and now I humbly ask for help!
i have been trying to check the UCI bit (UCI, bit [26]) in SCTLR_EL1 Register on a Corte-A72 ARMv8 RUnning linux.
So i wrote a simple kernel module :
#include <linux/module.h>
#include<linux/init.h>
#include <linux/kernel.h>
static inline void write_SCTLR_EL1(uint64_t val){
asm volatile("msr s3_0_c1_c0_0 , %0" : : "r" (val));
asm volatile("isb" : : : "memory");
}
static inline uint64_t read_SCTLR_EL1(void){
uint64_t val;
asm volatile("mrs %0, s3_0_c1_c0_0" : "=r" (val));
return val;
}
int __init enable_cache_instructions(void){
uint64_t value = read_SCTLR_EL1();
printk(KERN_INFO "VALUE IS <0x%016llx> from SCTLR_EL1.", value);
return 0;
}
void disable_cache(void) {
printk(KERN_INFO "Instructions disabled \n");
}
module_init(enable_cache_instructions);
module_exit(disable_cache);
MODULE_LICENSE("GPL");
and the value of the Register is : 0x0000000034d5d83d (binary format = 110100110101011101100000111101).
So i wrote a simple c code to check UCI bit value :
int main (){
unsigned long register_value = 886429757; // decimal format of the Register value
register_value |= 1ULL << 26; //trying to set UCI bit to 1 if it is disabled
printf("After set : %lu\n",register_value);
return 0;
}
Output : 886429757
Is that right ? The value of UCI bit is 1 ?
i mean, if the value is not set, the start and the end values should be different.
i tried checking the value in c code as well :
register_value &= (1ULL << 26);
printf("After check : %lu\n",register_value);
but the output is : 67108864.
Thank You.
I have a macbookpro11,3 without a battery. When battery is removed the firmware throttles the CPU to half speed. In Windows I can override this using Throttlestop to turn off BD PROCHOT and set multiplier to 25. I want to do this from EFI so that boot and updates run at a normal speed.
Based on source for rEFInd which updates 0x3a register I wrote this program but while BD PROCHOT is disabled correctly after booting into Windows the multiplier is not.
#include "../include/tiano_includes.h"
static VOID DisablePROCHOT(VOID)
{
UINT32 msr = 0x1FC;
UINT32 low_bits = 0, high_bits = 0;
__asm__ volatile ("rdmsr" : "=a" (low_bits), "=d" (high_bits) : "c" (msr));
// lowest bit is BD PROCHOT
low_bits &= ~(1 << 0);
__asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits));
} // VOID DisablePROCHOT()
static VOID SetMultiplier25(VOID)
{
UINT32 msr = 0x199;
UINT32 low_bits = 0, high_bits = 0;
__asm__ volatile ("rdmsr" : "=a" (low_bits), "=d" (high_bits) : "c" (msr));
// second lowest byte is multiplier
// 25 is .... xxxxxxxx 00011001 xxxxxxxx
low_bits |= 1 << 8;
low_bits &= ~(1 << 9);
low_bits &= ~(1 << 10);
low_bits |= 1 << 11;
low_bits |= 1 << 12;
low_bits &= ~(1 << 13);
low_bits &= ~(1 << 14);
low_bits &= ~(1 << 15);
__asm__ volatile ("wrmsr" : : "c" (msr), "a" (low_bits), "d" (high_bits));
} // VOID SetMultiplier25()
EFI_STATUS
EFIAPI
efi_main (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
DisablePROCHOT();
SetMultiplier25();
return EFI_SUCCESS;
}
Reading the registers with rdmsr from EFI appears to show both are set correctly however when booted into Windows while bit 0 of 0x1FC is correctly set off the multiplier stored in 0x199 is unchanged from the default of 12 when I expect it to be 25.
Default values
These are values after standard boot into Windows (from RWEverything)
Results after calling program
Program was called from EFI shell before calling Windows boot loader bootmgfw.efi
0x1FC is updated, 0x199 is not.
Updating 0x199 with RWEverything from within Windows changes the multiplier correctly so I'm fairly sure it is the correct register.
As this is my first EFI (or C) program I may have overlooked something trivial.
You have to create a loop and change processor affinity each time through the loop. Then you do a wrmsr for each thread (CPU1, CPU2, CPU3, CPU4) each time through the loop. In Windows you use this function.
https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-setthreadaffinitymask
As soon as you boot up, Windows changes the values in MSR 0x199 so seeing what values are in MSR 0x199 after you boot up does not prove anything.
To simplify things, you can do this in SetMultiplier,
low_bits = 0x1900
I'm trying to write a context switch in a timer interrupt handler. Currently, the context switch is able to switch between contexts on command (cooperative). In the interrupt handler, I was trying to:
Save the current program counter as the place the old thread needs to keep executing
Switch into SVC mode to actually perform the context switch
Switch back into IRQ mode and change the link register to be the saved PC from the new thread
Return from the IRQ handler to the IRQ link register
I believe I can do the first two properly, but I was wondering: how can I switch back into interrupt mode, or at least modify the SVC R13 and R15 from the interrupt handler?
I'm using an ARM v6 processor; thanks so much for the help!
Edit: here's basically what my switch is:
void interrupt_yield() {
unsigned int old_mode;
__asm__("mrs %0, cpsr" : "=r" (old_mode));
__asm__("msr cpsr_c, %0" : : "r" (MODE_SVC));
PUSH_ALL; // Macro for push {r0-r12, lr}
__asm__("mov %0, sp" : "=r"(sp));
manager->threads[manager->current_thread].sp = sp;
unsigned nt = (manager->current_thread + 1) % manager->thread_counter;
if (CURRENT_THREAD.status == ACTIVE) {
CURRENT_THREAD.status = INACTIVE;
}
manager->current_thread = nt;
CURRENT_THREAD.status = ACTIVE;
SET_SP(CURRENT_THREAD.sp);
POP_ALL;
__asm__("msr cpsr, %0" : : "r" (old_mode));
}
void timer_vector() { // This is called by assembly in interrupt mode
armtimer_clear_interrupt(); // clear timer interrupt
interrupt_yield(); // Calls above function
}
The goal is to change the IRQ link register to return to the new function. I can't seem to switch back into interrupt mode, however, to do this.
1 more edit: I never actually switch the IRQ link register; I realize this but am not even switching back into IRQ mode so this is a later problem to fix.
For the ARMv6 you need to change modes to get the banked registers. Your sample code already has many of the necessary details.
#define MODE_IRQ 0x12
#define MODE_SVC 0x13
unsigned int mode; /* original mode */
/* target data... */
unsigned int lr_irq;
unsigned int sp_irq;
unsigned int spsr;
asm (" mrs %0, cpsr\n" /* Save mode. */
" msr cpsr_c,%4 \n" /* to irq mode */
" mov %1, lr\n" /* Get lr_irq */
" mov %2, sp\n" /* Get sp_irq */
" mrs %3, spsr\n" /* Get spsr_irq */
" msr cpsr, %0\n" /* back to old mode */
: "=&r" (mode), "=r"(lr_irq),
"=r"(sp_irq), "=r"(spsr)
: "I" (MODE_IRQ));
gcc will allocate the lr_irq etc to general registers (non-banked) and you can transfer the data across modes. The ARMv7 with virtualization extensions has an instruction to avoid this switch.
You should be aware that the timer interrupt could occur in many contexts. It is probably prudent to at least check the spsr from the IRQ mode and have some debug (assert like) that verifies it is user mode. If this never triggers and you think an IRQ can only happen in user mode then the 'debug' can be removed.
Another method is to do this in the assembler of the IRQ handler and pass them to the interrupt_yield() routine in r0-r2 for instance. The ARM EABI puts parameters in r0-r2 so interrupt yield needs parameters. Once you have this data there should be no need to return to the IRQ mode. I highly recommend this method for production code. The above is good for prototyping.
Related: Explicitly accessing banked registers on ARM
I'm using a Exynos 3110 processor (1 GHz Single-core ARM Cortex-A8, e.g. used in the Nexus S) and try to measure execution times of particular functions. I have an Android 4.0.3 running on the Nexus S. I tried the method from
[1] How to measure program execution time in ARM Cortex-A8 processor?
I loaded the kernel module to allow reading the register values in user mode. I am using the following program to test the counter:
static inline unsigned int get_cyclecount (void)
{
unsigned int value;
// Read CCNT Register
asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(value));
return value;
}
static inline void init_perfcounters (int do_reset, int enable_divider)
{
// in general enable all counters (including cycle counter)
int value = 1;
// peform reset:
if (do_reset)
{
value |= 2; // reset all counters to zero.
value |= 4; // reset cycle counter to zero.
}
if (enable_divider)
value |= 8; // enable "by 64" divider for CCNT.
value |= 16;
// program the performance-counter control-register:
asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(value));
// enable all counters:
asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000000f));
// clear overflows:
asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000000f));
}
int main(int argc, char **argv)
{
int i = 0;
unsigned int start = 0;
unsigned int end = 0;
printf("Hello Counter\n");
init_perfcounters(1,0);
for(i=0;i<10;i++)
{
start = get_cyclecount();
sleep(1); // sleep one second
end = get_cyclecount();
printf("%u %u %u\n", start, end, end - start);
}
return 0;
}
According to [1] the counter is incremented with each clock cycle. I switched the scaling_governor to userspace and set the CPU frequency to 1GHz to make sure that the clock frequency is not change by Android.
If I run the program the sleeps of 1 second are executed, but the counter values are in the range of ~200e6, instead of the expected 1e9. Is there anything processor specific I am missing here? Is the clock rate of the counters different to the clock rate of the processor ?
Check out this professor's page: http://users.ece.utexas.edu/~valvano/arm/
He has multiple full example programs that have to do with time/periodic-timers/measuring-execution-time, they are developed for ARM Cortex-M3 based microcontrollers. I hope this isn't very different from what you are working on.
I think you would be interested in Performance.c
Are you sure governors are used in Android for performance management the same way that in standard Linux? And are you using custom Android image or one provided by manufacturer? I would assume there are lower level policies in place in manufacturer provided image (tied to sleeps or modem activity, etc). It could be also that sleep code directly scales voltage and frequency. It might be worthwhile to disable the whole CPUFreq not just the policies (or governors).