I am simulating a CPU and I'm doing this using high level simulation tools. SystemC is a good resource for these purposes. I'm using two modules:
DataPath
Memory
CPU datapath is modeled as a unique high level entity, however the following code will sure be better than any other explaination:
The following is datapath.hpp
SC_MODULE(DataPath) {
sc_in_clk clk;
sc_in<bool> rst;
///
/// Outgoing data from memory.
///
sc_in<w32> mem_data;
///
/// Memory read enable control signal.
///
sc_out<sc_logic> mem_ctr_memreadenable;
///
/// Memory write enable control signal.
///
sc_out<sc_logic> mem_ctr_memwriteenable;
///
/// Data to be written in memory.
///
sc_out<w32> mem_dataw; //w32 is sc_lv<32>
///
/// Address in mem to read and write.
///
sc_out<memaddr> mem_addr;
///
/// Program counter.
///
sc_signal<w32> pc;
///
/// State signal.
///
sc_signal<int> cu_state;
///
/// Other internal signals mapping registers' value.
/// ...
// Defining process functions
///
/// Clock driven process to change state.
///
void state_process();
///
/// State driven process to apply control signals.
///
void control_process();
// Constructors
SC_CTOR(DataPath) {
// Defining first process
SC_CTHREAD(state_process, clk.neg());
reset_signal_is(this->rst, true);
// Defining second process
SC_METHOD(control_process);
sensitive << (this->cu_state) << (this->rst);
}
// Defining general functions
void reset_signals();
};
The following is datapath.cpp
void DataPath::state_process() {
// Useful variables
w32 ir_value; /* Placing here IR register value */
// Initialization phase
this->cu_state.write(StateFetch); /* StateFetch is a constant */
wait(); /* Wait next clock fall edge */
// Cycling
for (;;) {
// Checking state
switch (this->cu_state.read()) { // Basing on state, let's change the next one
case StateFetch: /* FETCH */
this->cu_state.write(StateDecode); /* Transition to DECODE */
break;
case StateDecode: /* DECODE */
// Doing decode
break;
case StateExecR: /* EXEC R */
// For every state, manage transition to the next state
break;
//...
//...
default: /* Possible not recognized state */
this->cu_state.write(StateFetch); /* Come back to fetch */
} /* switch */
// After doing, wait for the next clock fall edge
wait();
} /* for */
} /* function */
// State driven process for managing signal assignment
// This is a method process
void DataPath::control_process() {
// If reset signal is up then CU must be resetted
if (this->rst.read()) {
// Reset
this->reset_signals(); /* Initializing signals */
} else {
// No Reset
// Switching on state
switch (this->cu_state.read()) {
case StateFetch: /* FETCH */
// Managing memory address and instruction fetch to place in IR
this->mem_ctr_memreadenable.write(logic_sgm_1); /* Enabling memory to be read */
this->mem_ctr_memwriteenable.write(logic_sgm_0); /* Disabling memory from being written */
std::cout << "Entering fetch, memread=" << this->mem_ctr_memreadenable.read() << " memwrite=" << this->mem_ctr_memreadenable.read() << std::endl;
// Here I read from memory and get the instruction with some code that you do not need to worry about because my problem occurs HERE ###
break;
case kCUStateDecode: /* DECODE */
// ...
break;
//...
//...
default: /* Unrecognized */
newpc = "00000000000000000000000000000000";
} /* state switch */
} /* rst if */
} /* function */
// Resetting signals
void DataPath::reset_signals() {
// Out signals
this->mem_ctr_memreadenable.write(logic_sgm_1);
this->mem_ctr_memwriteenable.write(logic_sgm_0);
}
As you can see we have a clock driven process that handles cpu transitions (changing state) and a state driven process that sets signals for cpu.
My problem is that when I arrive in ### I expect the instruction being released by memory (you cannot see the instructions but they are correct, the memory component is connected to datapath using in and out singals you can see in the hpp file).
Memory gets me "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" because mem_ctr_memreadenable and mem_ctr_memwriteenable are both set to '0'.
Memory module is written in order to be an instant component. It is written using a SC_METHOD whose sensitive is defined on input signals (read enable and write enable included). The memory component gets "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" when the mem_ctr_memreadenable signal is '0'.
Why is it '0'? I reset signals and set that signal to '1'. I do not understand why I keep having '0' for the read enable signal.
Can you help me?
Thankyou.
I'm no SystemC guru, but it looks like it might be a similar problem to a common VHDL problem of signals not updating until at least a delta-cycle has passed:
this->mem_ctr_memreadenable.write(logic_sgm_1); /* Enabling memory to be read */
this->mem_ctr_memwriteenable.write(logic_sgm_0); /* Disabling memory from being written */
My guess: No time passes between these two lines and this next line:
std::cout << "Entering fetch, memread=" << this->mem_ctr_memreadenable.read() << " memwrite=" << this->mem_ctr_memreadenable.read() << std::endl;
So the memory hasn't yet seen the read signal change. BTW, should one of the read() calls attached to mem_ctr_memwriteenable - the both seem to be on readenable?
If you:
wait(1, SC_NS);
between those two points, does it improve matters?
To get a zero time synchronization with the memory module you should use
wait(SC_ZERO_TIME); //wait one delta cycle
not to introduce an arbitrary consumtion of time in your timed simulation.
This also impose you upgrade your control_process to an SC_THREAD
Related
I am trying to write on the flash memory of an STM32L476RG with the HAL_FLASH_Program function but it always returns an error.
static FLASH_EraseInitTypeDef EraseInitStruct;
uint32_t PAGEError;
uint32_t Address = 0x080FFF10;
uint64_t data = 5;
/* Unlock the Flash to enable the flash control register access *************/
HAL_FLASH_Unlock();
/* Erase the user Flash area*/
/* Fill EraseInit structure*/
EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInitStruct.Page = Address;
EraseInitStruct.NbPages = 1;
if (HAL_FLASHEx_Erase(&EraseInitStruct, &PAGEError) != HAL_OK)
{
/*Error occurred while page erase.*/
HAL_FLASH_GetError ();
}
/*Write into flash*/
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x1FFF7000, data);
if (status== HAL_OK)
{
printf("it works\n\r");
}
else
{
/* Error occurred while writing data in Flash memory*/
HAL_FLASH_GetError();
}
HAL_FLASH_Lock();
I tried to find wthe flash error code with the HAL_FLASH_GetError() function.
The error code I get is "168" (0xa8 in Hex) and I have no idea to what it corresponds.
My questions :
What error is the code 168 (0xa8 in Hex)
what do i need to change so that HAL_FLASH_Program works properly
The problem is how the fields in EraseInitStruct are being set. The HAL driver for some STM32 parts expects an address. However, the HAL library for the STM32L476 expects a page number.
typedef struct
{
uint32_t TypeErase; /*!< Mass erase or page erase.
This parameter can be a value of #ref FLASH_Type_Erase */
uint32_t Banks; /*!< Select bank to erase.
This parameter must be a value of #ref FLASH_Banks
(FLASH_BANK_BOTH should be used only for mass erase) */
uint32_t Page; /*!< Initial Flash page to erase when page erase is disabled
This parameter must be a value between 0 and (max number of pages in the bank - 1)
(eg : 255 for 1MB dual bank) */
uint32_t NbPages; /*!< Number of pages to be erased.
This parameter must be a value between 1 and (max number of pages in the bank - value of initial page)*/
} FLASH_EraseInitTypeDef;
So you need to set the page number correctly, and also specify which flash bank you are trying to erase:
EraseInitStruct.Banks = FLASH_BANK_2;
EraseInitStruct.Page = 255u;
It is good practice to check the result of all HAL function calls, and abort the operation if there is an error.
#Lundin brought up a good point about possibly being unable to erase / program the flash bank that you are running code from. This is an issue for some devices, but the reference manual for the STM32L476 (in section 3.3.5) says this is ok:
... during a program/erase operation to the Flash memory, any attempt to read the same Flash memory bank will stall the bus. The read operation will proceed correctly once the program/erase operation has completed.
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#define REPORTING_PERIOD_MS 1000
// PulseOximeter is the higher level interface to the sensor
// it offers:
// * beat detection reporting
// * heart rate calculation
// * SpO2 (oxidation level) calculation
PulseOximeter pox;
uint32_t tsLastReport = 0;
// Callback (registered below) fired when a pulse is detected
void onBeatDetected()
{
Serial.println("Beat!");
}
void setup()
{
Serial.begin(115200);
Serial.print("Initializing pulse oximeter..");
// Initialize the PulseOximeter instance
// Failures are generally due to an improper I2C wiring, missing power supply
// or wrong target chip
if (!pox.begin()) {
Serial.println("FAILED");
for(;;);
} else {
Serial.println("SUCCESS");
}
// The default current for the IR LED is 50mA and it could be changed
// by uncommenting the following line. Check MAX30100_Registers.h for all the
// available options.
// pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
// Register a callback for the beat detection
pox.setOnBeatDetectedCallback(onBeatDetected);
}
void loop()
{
// Make sure to call update as fast as possible
pox.update();
// Asynchronously dump heart rate and oxidation levels to the serial
// For both, a value of 0 means "invalid"
if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart rate:");
Serial.print(pox.getHeartRate());
Serial.print("bpm / SpO2:");
Serial.print(pox.getSpO2());
Serial.println("%");
tsLastReport = millis();
}
}
I want to output oxygen saturation with Arduino.
If you run it and turn on the serial monitor, only the Initializing pulse oximeter works, and no data is transmitted after that.
I want to output the values โโof oxygen saturation and pulse received from the sensor once per second on the serial monitor.
I am currently trying to use the performance monitor to generate an interrupt when an overflow of Data Cache misses occurs. I have enabled the pmu and the IRQ for the performance monitor (PMINTENSET is 1 for the counter). I am able to see that the overflow flag is set when the overflow occurs but the interrupt is never triggered. I think I am missing something when setting up the interrupt. I am using Xilinx SDK 2018.2.
I have attached my code for setting up the interrupt:
XScuGic xInterruptController; /* Interrupt controller instance */
static void setup_interrupt(void)
{
uint32_t status;
XScuGic_Config *pxGICConfig;
pxGICConfig = XScuGic_LookupConfig( XPAR_SCUGIC_0_DEVICE_ID );
if (pxGICConfig==NULL)
{
xil_printf("\nERROR LOOKING UP CONFIGURATION");
for(;;);
}
status = XScuGic_CfgInitialize( &xInterruptController, pxGICConfig, pxGICConfig->CpuBaseAddress );
if (status != XST_SUCCESS)
{
xil_printf("\nERROR INITIALIZING CONFIGURATION");
for(;;);
}
status = XScuGic_SelfTest(&xInterruptController);
if (status != XST_SUCCESS)
{
xil_printf("\nERROR: SELF TEST FAILURE");
for(;;);
}
/*
* Initialize the exception table.
*/
Xil_ExceptionInit();
status = RegisterInterruptExceptions(&xInterruptController);
if (status != XST_SUCCESS) {
xil_printf("\nERROR: SetUP Interrupt System Failed");
for(;;);
}
status = XScuGic_Connect( &xInterruptController, XPS_PMU0_INT_ID, (Xil_ExceptionHandler) pmuIRQ_handler, ( void * ) &xInterruptController);
if (status!= XST_SUCCESS)
{
xil_printf("\nERROR CONNECTING INTERRUPT");
for(;;);
}
XScuGic_SetPriorityTriggerType(&xInterruptController, XPS_PMU0_INT_ID, 8, 0b10); // Priority 8 (second highest) and high level sensitivity
XScuGic_InterruptMaptoCpu(&xInterruptController, 0, XPS_PMU0_INT_ID);
// Enable the interrupt for the xTimer in the interrupt controller.
XScuGic_Enable( &xInterruptController, XPS_PMU0_INT_ID );
}
int RegisterInterruptExceptions(XScuGic *XScuGicInstancePtr)
{
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the ARM processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler,XScuGicInstancePtr);
/*
* Enable interrupts in the ARM
*/
Xil_ExceptionEnable();
return XST_SUCCESS;
}
void pmuIRQ_handler( void *CallbackRef )
{
xil_printf("Interrupt occurred\n");
}
I am not sure if I need to use Vivado to map the PMU interrupt to the GIC? I couldn't find any examples on generating interrupts using the performance monitor. I am currently using the default ZC706 HW platform provided by Xilinx SDK and I am not sure if I need to generate a bitstream in Vivado the maps the PMU to the GIC? I thought that this was done by using XScuGic_InterruptMaptoCpu().
I tried with both XPS_PMU0_INT_ID and XPS_PMU1_INT_ID, but neither worked. I tried to follow this post on using shared peripheral interrupts since PMU is this type of interrupt: https://forums.xilinx.com/t5/Processor-System-Design-and-AXI/Using-Private-and-Shared-interrupts-on-Zynq/m-p/773673
Thanks for the help,
Javier
The last parameter is incorrect. It should be 0b01 For high-level sensitivity instead of 0b10, as shown below:
XScuGic_SetPriorityTriggerType(&xInterruptController, XPS_PMU0_INT_ID, 8, 0b01); // Priority 8 (second highest) and high level sensitivity
I want to send 10 bytes of data through UART loopback by interrupt mode. Though I'm able to receive the data, I don't understand why my HAL_UART_RxCpltCallback() is not getting executed. But my HAL_UART_TxCpltCallback(), is getting executed every time after a byte of data is transferred. I've generated code through cubeMX. I've enabled the USART2 global interrupt. My Control Register(CR1) for my USART2 is 0x300c. That is, UE,M,TE,RE bits are only set. The TXIE, TCIE bits are NOT set in the CR1 Register.
Now,
What does "global" in global interrupt mean here? Does that mean an interrupt is generated from any one of the 10 sources(mentioned in the data sheet like, Tx complete, Rx buffer full etc)?
None of the bits corresponding to interrupts are enabled in CR1 register. But the control is going to HAL_UART_TxCpltCallback() every time it transfers a byte of data. Why does this happen?
Please unveil the mystery.
PS: I'm using STM32F407G-DISC1 board, Attolic Truestudio IDE, Ubuntu 16.04 OS.
UART_HandleTypeDef huart2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
uint8_t tx_data[] = {0,1,2,3,4,5,6,7,8,9};
uint8_t rx_data[10];
int main(void)
{
int i=0;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
while (1)
{
HAL_UART_Transmit_IT(&huart2, &tx_data[i], 10);
HAL_Delay(1500);
HAL_UART_Receive_IT(&huart2, &rx_data[i] , 10);
HAL_Delay(1500);
if((xferCnt==10) || (rxCnt==10))
break;
else
i++;
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
xferCnt++;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
rxCnt++;
}
I've bought an STM32F411 nucleo board and now I'm trying to understand various bits and pieces of the HAL. Starting with external interrupts seemed to be a good idea, because the board has a push button which is connected to PC13. So I've set up a simple toggle-the-frequency blinky. The code below is a bit simplified:
#define LED_PIN GPIO_PIN_5
#define BTN_PIN GPIO_PIN_13
static uint32_t blink_period = 250;
int main(void)
{
HAL_Init();
SystemClock_Config();
__GPIOA_CLK_ENABLE();
GPIO_InitTypeDef pinConfig;
pinConfig.Pin = (LED_PIN);
pinConfig.Pull = GPIO_NOPULL;
pinConfig.Mode = GPIO_MODE_OUTPUT_PP;
pinConfig.Speed = GPIO_SPEED_FAST;
HAL_GPIO_Init(GPIOA, &pinConfig);
__GPIOC_CLK_ENABLE();
pinConfig.Pin = (BTN_PIN);
pinConfig.Pull = GPIO_NOPULL;
pinConfig.Mode = GPIO_MODE_IT_FALLING;
pinConfig.Speed = GPIO_SPEED_LOW;
HAL_GPIO_Init(GPIOC, &pinConfig);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0x0F, 0x00);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
while (1)
{
HAL_GPIO_TogglePin(GPIOA, LED_PIN);
HAL_Delay(blink_period);
}
}
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(BTN_PIN);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == BTN_PIN)
{
if (blink_period == 500)
{
blink_period = 250;
}
else
{
blink_period = 500;
}
}
}
When I push the button, an interrupt is generated and the blinky frequency changes from 1 to 2 Hz (or vice-versa). This works as intended, but why? I forgot to clear the pending interrupt flag, so the ISR should be called over and over. The datasheet clearly states that
When the selected edge occurs on the external interrupt line, an interrupt request is generated. The pending bit corresponding to the interrupt line is also set. This request is
reset by writing a โ1โ in the pending register.
Reading a bit further reveals that this is a bit different for events:
When the selected edge occurs on the event line, an event pulse is generated. The pending bit corresponding to the event line is not set.
However, I'm not setting the button pin mode to any of the GPIO_MODE_EVT_... modes so I'm not using the event mechanism (to be honest I don't yet know what that even is - I just think that I'm not using it. Any hints are welcome).
So somewhere I should have to call void HAL_NVIC_ClearPendingIRQ (IRQn_Type IRQn), shouldn't I? It seems that clearing the flag by software is not necessary, because the ISR is not called more than once per falling edge. I've added a breakpoint in HAL_GPIO_EXTI_Callback to verify this.
Edit
As mentioned in the comments, the flag clearing code is in ST's implementation of the GPIO interrupt handler:
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
/* EXTI line interrupt detected */
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}
This handler needs to be called by the actual ISR (which is done in my code) and it clears the pending flag corresponding to the GPIO_Pin argument. So I have to write an ISR which sorts out which flags are set, and call HAL_GPIO_EXTI_IRQHandler for each, which in turn calls my HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin), again with the pin as an argument. For each external interrupt, the pin number would get checked some ~3 times (in the ISR, in the handler and in the callback)!
If that is the solution, I want my problem back.
You don't have to call HAL_NVIC_ClearPendingIRQ (IRQn_Type IRQn) because the pending bit in the NVIC will be cleared automatically upon entering HAL_GPIO_EXTI_IRQHandler.
The HAL_GPIO_EXTI_IRQHandler() implementation clears the pending bit in the peripheral, not in the NVIC. If it didn't clear the pending bit by calling __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin), then the handler would be called again and again. The point is that you must distinguish between the interrupt pending bit in the peripheral and the pending bit in the NVIC.