Skip to content

Commit e975816

Browse files
committed
Optimize a few high-frequency functions
1 parent 916791a commit e975816

15 files changed

+95
-80
lines changed

hw/arm/prusa/stm32_common/stm32_adc.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ static void stm32_adc_schedule_next(COM_STRUCT_NAME(Adc) *s) {
345345
}
346346

347347
// Calculate the clock rate
348-
uint64_t clock = stm32_rcc_if_get_periph_freq(&s->parent);
348+
uint64_t clock = s->parent.clock_freq;
349349

350350
if (s->adcc)
351351
{

hw/arm/prusa/stm32_common/stm32_common.c

+14-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414
#include "stm32_rcc_if.h"
1515
#include "stm32_shared.h"
1616

17-
DECLARE_CLASS_CHECKERS(STM32PeripheralClass, STM32_PERIPHERAL, TYPE_STM32_PERIPHERAL);
18-
19-
DECLARE_INSTANCE_CHECKER(STM32Peripheral, STM32_PERIPHERAL, TYPE_STM32_PERIPHERAL);
20-
2117
static bool create_if_not_exist(const char* default_name, uint32_t file_size)
2218
{
2319
bool exists = true;
@@ -178,6 +174,15 @@ extern DeviceState* stm32_soc_get_periph(DeviceState* soc, stm32_periph_t id)
178174
return s->perhiperhals[id];
179175
}
180176

177+
static void stm32_peripheral_clock_change(void *opaque, int n, int level)
178+
{
179+
STM32Peripheral *p = STM32_PERIPHERAL(opaque);
180+
p->clock_enabled = stm32_rcc_if_check_periph_clk(p);
181+
p->clock_freq = stm32_rcc_if_get_periph_freq(p);
182+
STM32PeripheralClass *c = STM32_PERIPHERAL_GET_CLASS(opaque);
183+
if (c->clock_update)
184+
c->clock_update(p);
185+
}
181186

182187
static void stm32_peripheral_rcc_reset(void *opaque, int n, int level)
183188
{
@@ -274,6 +279,10 @@ extern void stm32_soc_realize_peripheral(DeviceState* soc_state, stm32_periph_t
274279
sysbus_mmio_map(SYS_BUS_DEVICE(s->perhiperhals[id]), 0, cfg->base_addr);
275280
}
276281
}
282+
if (stm32_rcc_if_has_clk(STM32_PERIPHERAL(s->perhiperhals[id])))
283+
{
284+
stm32_rcc_if_set_periph_clk_irq(STM32_PERIPHERAL(s->perhiperhals[id]), qdev_get_gpio_in_named(s->perhiperhals[id],"clock-change",0));
285+
}
277286
for (const int *irq = cfg->irq; *irq != -1; irq++)
278287
{
279288
sysbus_connect_irq(SYS_BUS_DEVICE(s->perhiperhals[id]), irq-(cfg->irq), qdev_get_gpio_in(s->cpu, *irq));
@@ -300,6 +309,7 @@ extern void stm32_soc_realize_all_peripherals(DeviceState *soc_state,Error **err
300309
static void stm32_peripheral_instance_init(Object* obj)
301310
{
302311
qdev_init_gpio_in_named(DEVICE(obj), stm32_peripheral_rcc_reset, "rcc-reset",1);
312+
qdev_init_gpio_in_named(DEVICE(obj), stm32_peripheral_clock_change, "clock-change",1);
303313
STM32Peripheral *s = STM32_PERIPHERAL(obj);
304314
qdev_init_gpio_out_named(DEVICE(obj),s->dmar,"dmar",2);
305315
}

hw/arm/prusa/stm32_common/stm32_common.h

+9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ typedef struct STM32Peripheral
3434

3535
struct STM32COMRccState* rcc; // RCC interfacing with clocktree.
3636

37+
// The clock frequency as output from the RCC
38+
uint32_t clock_freq;
39+
bool clock_enabled;
3740
/* DMA IRQ. To send a periperhal DMA Request, set the level to the peripheral's data register.
3841
* The DMA controller will check against its active streams for a match. and service it if one is found.
3942
* Use index 1 if this is meant to be a write (M2P) or "ready for data" case - some peripherals have
@@ -43,6 +46,8 @@ typedef struct STM32Peripheral
4346

4447
} STM32Peripheral;
4548

49+
DECLARE_INSTANCE_CHECKER(STM32Peripheral, STM32_PERIPHERAL, TYPE_STM32_PERIPHERAL);
50+
4651
enum DMAR_TYPE {
4752
DMAR_TYPE_BEGIN,
4853
DMAR_P2M = 0, // Read operation, i.e. DMA takes data from the device.
@@ -58,8 +63,12 @@ enum EXTI_TRANS {
5863
typedef struct STM32PeripheralClass
5964
{
6065
SysBusDeviceClass parent;
66+
// A handler to set a callback if the input clock changes.
67+
void (*clock_update)(STM32Peripheral *p);
6168
} STM32PeripheralClass;
6269

70+
DECLARE_CLASS_CHECKERS(STM32PeripheralClass, STM32_PERIPHERAL, TYPE_STM32_PERIPHERAL);
71+
6372
// Common class data for variant storage.
6473

6574
// Machine class templates:

hw/arm/prusa/stm32_common/stm32_crc.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static unsigned long crctable[256] =
140140
};
141141

142142
typedef struct COM_CLASS_NAME(Crc) {
143-
SysBusDeviceClass parent_class;
143+
STM32PeripheralClass parent_class;
144144
stm32_reginfo_t var_reginfo[RI_END];
145145
} COM_CLASS_NAME(Crc);
146146

hw/arm/prusa/stm32_common/stm32_dma.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ enum interrupt_bits
143143
static const uint8_t dma_xfer_size_b[4] = {1, 2, 4, 0};
144144

145145
typedef struct COM_CLASS_NAME(Dma) {
146-
SysBusDeviceClass parent_class;
146+
STM32PeripheralClass parent_class;
147147
stm32_reginfo_t var_reginfo[RI_END];
148148
} COM_CLASS_NAME(Dma);
149149

hw/arm/prusa/stm32_common/stm32_gpio.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ typedef struct COM_STRUCT_NAME(Gpio) {
6969
} COM_STRUCT_NAME(Gpio);
7070

7171
typedef struct COM_CLASS_NAME(Gpio) {
72-
SysBusDeviceClass parent_class;
72+
STM32PeripheralClass parent_class;
7373
stm32_reginfo_t var_reginfo[MAX_GPIO_BANKS][RI_END];
7474
} COM_CLASS_NAME(Gpio);
7575

hw/arm/prusa/stm32_common/stm32_iwdg.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ static void stm32_common_iwdg_update(COM_STRUCT_NAME(Iwdg) *s){
135135
if (!s->started){
136136
return;
137137
}
138-
uint32_t clkrate = stm32_rcc_if_get_periph_freq(&s->parent);
138+
uint32_t clkrate = s->parent.clock_freq;
139139
if (clkrate == 0)
140140
{
141141
qemu_log_mask(LOG_GUEST_ERROR,"ERR: Attempted to enable IWDG with LSI clock disabled!\n");

hw/arm/prusa/stm32_common/stm32_rcc.c

+12
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ bool stm32_rcc_if_check_periph_clk(STM32Peripheral *p)
7676
return true;
7777
}
7878

79+
bool stm32_rcc_if_has_clk(STM32Peripheral *p)
80+
{
81+
if (p->rcc == NULL)
82+
{
83+
return 0;
84+
}
85+
COM_STRUCT_NAME(Rcc) *s = STM32COM_RCC(p->rcc);
86+
Clk_t* clk = &s->pclocks[p->periph];
87+
88+
return clk->is_initialized;
89+
}
90+
7991
void stm32_rcc_if_set_periph_clk_irq(
8092
STM32Peripheral *p,
8193
qemu_irq periph_irq)

hw/arm/prusa/stm32_common/stm32_rcc_if.h

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ extern uint32_t stm32_rcc_if_get_periph_freq(STM32Peripheral *p);
3030
// Checks if a clock is enabled and prints a guest error if not
3131
extern bool stm32_rcc_if_check_periph_clk(STM32Peripheral *p);
3232

33+
// Checks if a clock is initialized and prints a guest error if not
34+
extern bool stm32_rcc_if_has_clk(STM32Peripheral *p);
35+
3336
/* Sets the IRQ to be called when the specified peripheral clock changes
3437
* frequency. */
3538
extern void stm32_rcc_if_set_periph_clk_irq(

hw/arm/prusa/stm32_common/stm32_usart.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ static const uint16_t PRESCALE_DIV[] = {
315315
/* Update the baud rate based on the USART's peripheral clock frequency. */
316316
static void stm32_common_usart_baud_update(COM_STRUCT_NAME(Usart) *s)
317317
{
318-
uint32_t clk_freq = stm32_rcc_if_get_periph_freq(&s->parent);
318+
uint32_t clk_freq = s->parent.clock_freq;
319319

320320
uint64_t ns_per_bit;
321321

hw/arm/prusa/stm32f407/stm32_uart.c

+7-20
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@
6767
/* HELPER FUNCTIONS */
6868

6969
/* Update the baud rate based on the USART's peripheral clock frequency. */
70-
static void stm32_uart_baud_update(Stm32Uart *s)
70+
static void stm32_uart_baud_update(STM32Peripheral *p)
7171
{
72-
uint32_t clk_freq = stm32_rcc_if_get_periph_freq(&s->parent);
72+
Stm32Uart *s = STM32_UART(p);
73+
uint32_t clk_freq = s->parent.clock_freq;
7374

7475
uint64_t ns_per_bit;
7576

@@ -101,19 +102,6 @@ static void stm32_uart_baud_update(Stm32Uart *s)
101102
#endif
102103
}
103104

104-
/* Handle a change in the peripheral clock. */
105-
static void stm32_uart_clk_irq_handler(void *opaque, int n, int level)
106-
{
107-
Stm32Uart *s = STM32_UART(opaque);
108-
109-
assert(n == 0);
110-
111-
/* Only update the BAUD rate if the IRQ is being set. */
112-
if(level) {
113-
stm32_uart_baud_update(s);
114-
}
115-
}
116-
117105
/* Routine which updates the USART's IRQ. This should be called whenever
118106
* an interrupt-related flag is updated.
119107
*/
@@ -523,7 +511,7 @@ static void stm32_uart_write(void *opaque, hwaddr addr,
523511
break;
524512
case USART_BRR_OFFSET:
525513
s->regs[addr] = data;
526-
stm32_uart_baud_update(s);
514+
stm32_uart_baud_update(STM32_PERIPHERAL(s));
527515
break;
528516
case USART_CR1_OFFSET:
529517
case USART_CR3_OFFSET:
@@ -583,10 +571,6 @@ static void stm32_uart_init(Object *obj)
583571
timer_new_ns(QEMU_CLOCK_VIRTUAL,
584572
(QEMUTimerCB *)stm32_uart_idle_timer_expire, s);
585573

586-
/* Register handlers to handle updates to the USART's peripheral clock. */
587-
s->clk_irq =
588-
qemu_allocate_irqs(stm32_uart_clk_irq_handler, (void *)s, 1);
589-
stm32_rcc_if_set_periph_clk_irq(&s->parent, s->clk_irq[0]);
590574

591575
//stm32_uart_connect(s, &s->chr);
592576

@@ -662,6 +646,9 @@ static void stm32_uart_class_init(ObjectClass *klass, void *data)
662646
device_class_set_props(dc, stm32_uart_properties);
663647
dc->realize = stm32_uart_realize;
664648
dc->vmsd = &vmstate_stm32_uart;
649+
650+
STM32PeripheralClass *k = STM32_PERIPHERAL_CLASS(klass);
651+
k->clock_update = stm32_uart_baud_update;
665652
}
666653

667654
static TypeInfo stm32_uart_info = {

hw/arm/prusa/stm32f407/stm32f2xx_tim.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ enum OCxM_val
110110
static uint32_t
111111
f2xx_tim_period(f2xx_tim *s, uint64_t multiplier)
112112
{
113-
uint64_t clock_freq = stm32_rcc_if_get_periph_freq(&s->parent);
113+
uint64_t clock_freq = s->parent.clock_freq;
114114
clock_freq/= (s->defs.PSC+1);
115115
return muldiv64(1000000000ULL,multiplier,clock_freq);
116116
}
117117

118118
static inline int64_t f2xx_tim_ns_to_ticks(f2xx_tim *s, int64_t t)
119119
{
120-
uint64_t clock_freq = stm32_rcc_if_get_periph_freq(&s->parent);
120+
uint64_t clock_freq = s->parent.clock_freq;
121121
if (clock_freq == 0)
122122
return 0;
123123
else

hw/arm/prusa/stm32f407/stm32f4xx_adc.c

+40-46
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ struct STM32F4XXADCState {
174174

175175
uint8_t adc_sequence_position, adc_next_seq_pos;
176176

177+
// Pre-computed conversion times
178+
uint64_t adc_conv_times_ns[ADC_NUM_REG_CHANNELS];
179+
177180
QEMUTimer* next_eoc;
178181
};
179182

@@ -194,6 +197,8 @@ typedef union {
194197
} QEMU_PACKED;
195198
} adc_smpr_t;
196199

200+
static const uint16_t adc_smpr_map[] = { 3, 15, 28, 56, 84, 112, 144, 480 };
201+
197202
static void stm32f4xx_adc_reset(DeviceState *dev)
198203
{
199204
STM32F4XXADCState *s = STM32F4xx_ADC(dev);
@@ -210,29 +215,6 @@ static void stm32f4xx_adc_reset(DeviceState *dev)
210215
timer_del(s->next_eoc);
211216
}
212217

213-
static uint16_t adc_lookup_smpr(uint8_t value) {
214-
switch (value) {
215-
case 0:
216-
return 3;
217-
case 1:
218-
return 15;
219-
case 2:
220-
return 28;
221-
case 3:
222-
return 56;
223-
case 4:
224-
return 84;
225-
case 5:
226-
return 112;
227-
case 6:
228-
return 144;
229-
case 7:
230-
return 480;
231-
default:
232-
assert(false);
233-
return 0;
234-
}
235-
}
236218

237219
static uint32_t stm32f4xx_adc_get_value(STM32F4XXADCState *s)
238220
{
@@ -241,7 +223,8 @@ static uint32_t stm32f4xx_adc_get_value(STM32F4XXADCState *s)
241223
// I'm not sure why this is yet - some sort of built in oversampling
242224
// that is enabled in non-DMA mode?
243225
if (!s->defs.CR2.DMA) {
244-
s->defs.DR*=(adc_lookup_smpr(s->adc_smprs[channel])+1);
226+
assert(s->adc_smprs[channel] < ARRAY_SIZE(adc_smpr_map));
227+
s->defs.DR*=(adc_smpr_map[s->adc_smprs[channel]]+1);
245228
}
246229

247230
// Mask: RES 0..3 == 12..6 bit mask.
@@ -261,32 +244,33 @@ static void stm32f4xx_adc_data_in(void *opaque, int n, int level){
261244
// printf("ADC: Ch %d new data: %d\n",n, level);
262245
}
263246

264-
static void stm32f4xx_adc_schedule_next(STM32F4XXADCState *s) {
265-
if (!s->defs.CR2.ADON)
266-
return;
267-
s->defs.CR2.SWSTART = 0;
268-
// Calculate the clock rate
269-
uint64_t clock = stm32_rcc_if_get_periph_freq(&s->parent);
270-
247+
static void stm32f4xx_adc_recalc_times(STM32F4XXADCState *s) {
248+
// Get the clock rate
249+
uint64_t clock = s->parent.clock_freq;
271250
clock /= stm32f4xx_adcc_get_adcpre(s->common);
272-
273-
// #bits:
274251
uint32_t conv_cycles = (12U - (s->defs.CR1.RES<<1U));
275-
uint8_t channel = s->adc_sequence[s->adc_next_seq_pos];
276-
conv_cycles += adc_lookup_smpr(s->adc_smprs[channel]);
277-
278-
uint64_t delay_ns = 1000000000000U / (clock/conv_cycles);
279-
if (s->parent.periph == STM32_P_ADC3)
280-
{
252+
for (int i=0; i<ADC_NUM_REG_CHANNELS; i++)
253+
{
254+
assert(s->adc_smprs[i] < ARRAY_SIZE(adc_smpr_map));
255+
uint32_t ch_conv_cycles = conv_cycles + adc_smpr_map[s->adc_smprs[i]];
256+
s->adc_conv_times_ns[i] = 1000000000000U / (clock/ch_conv_cycles);
257+
if (s->parent.periph == STM32_P_ADC3)
258+
{
281259
// Yes, this is an ugly-ass hack. The above calc is off by 1000 and I still need to determine
282260
// how to deal with the other channels bogging down the simulation when they run at the "real" specified rate.
283-
delay_ns /= 1000;
261+
s->adc_conv_times_ns[i] /= 1000;
284262
//printf("ADC conversion: %u cycles @ %"PRIu64" Hz (%lu nSec)\n", conv_cycles, clock, delay_ns);
285-
}
286-
timer_mod_ns(s->next_eoc, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)+delay_ns);
287-
263+
}
264+
}
288265
}
289266

267+
static void stm32f4xx_adc_schedule_next(STM32F4XXADCState *s) {
268+
if (!s->defs.CR2.ADON)
269+
return;
270+
s->defs.CR2.SWSTART = 0;
271+
uint8_t channel = s->adc_sequence[s->adc_next_seq_pos];
272+
timer_mod_ns(s->next_eoc, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)+s->adc_conv_times_ns[channel]);
273+
}
290274

291275
static uint64_t stm32f4xx_adc_read(void *opaque, hwaddr addr,
292276
unsigned int size)
@@ -359,7 +343,7 @@ static void stm32f4xx_adc_update_sequence(STM32F4XXADCState *s)
359343
static void stm32f4xx_adc_convert(STM32F4XXADCState *s)
360344
{
361345
uint8_t channel = s->adc_sequence[s->adc_sequence_position];
362-
qemu_irq_pulse(s->irq_read[channel]); // Toggle the data read request IRQ. The receiver can opt to send a new value (or do nothing)
346+
qemu_irq_raise(s->irq_read[channel]); // Toggle the data read request IRQ. The receiver can opt to send a new value (or do nothing)
363347
}
364348

365349
static void stm32f4xx_adc_update_irqs(STM32F4XXADCState *s, int level) {
@@ -424,8 +408,17 @@ static void stm32f4xx_adc_write(void *opaque, hwaddr addr,
424408
}
425409

426410
switch (addr) {
427-
case RI_SR:
428411
case RI_CR1:
412+
{
413+
uint8_t old_res = s->defs.CR1.RES;
414+
s->regs[addr] = value;
415+
if (s->defs.CR1.RES != old_res)
416+
{
417+
stm32f4xx_adc_recalc_times(s);
418+
}
419+
}
420+
break;
421+
case RI_SR:
429422
case RI_HTR:
430423
case RI_LTR:
431424
s->regs[addr] = value;
@@ -450,6 +443,7 @@ static void stm32f4xx_adc_write(void *opaque, hwaddr addr,
450443
{
451444
s->adc_smprs[10+i] = (value >> (3*i)) & 0x7;
452445
}
446+
stm32f4xx_adc_recalc_times(s);
453447
break;
454448
case RI_SMPR2:
455449
// if (value!=0) printf("FIXME: Nonzero sample time\n");
@@ -458,7 +452,7 @@ static void stm32f4xx_adc_write(void *opaque, hwaddr addr,
458452
{
459453
s->adc_smprs[i] = (value >> (3*i)) & 0x7;
460454
}
461-
break;
455+
stm32f4xx_adc_recalc_times(s);
462456
break;
463457
case RI_JOFR1 ... RI_JOFR4:
464458
s->regs[addr] = (value & 0xFFF);

0 commit comments

Comments
 (0)