Digital Output assigned to the secondary CPU never drives the pin
(F28P65x dual-core) – bug, or are we missing something?
What we are seeing
On a dual-core F28P65x, a Digital Output block assigned to the secondary
CPU (CPU2) does not drive its pin. The model logic itself runs correctly, but
the pin shows no electrical activity. The exact same model with the same
block assigned to CPU1 works perfectly.
Looking at the generated code, we noticed that no
GPIO_setDirectionMode(..., GPIO_DIR_MODE_OUT) call is generated for that
pin – on either core – so the pin’s direction appears to stay at its reset
default (input). That would explain the symptom, since writes to the
output latch have no effect on a pin configured as an input.
We then dug into the Lua sources to understand why, and found something
that might explain it (see below). However, we are not sure whether this
is an actual bug or whether we are simply configuring something
incorrectly – dual-CPU operation is new to us. If there is a required
setting or a supported way to do this that we have missed, we would be
grateful for a pointer. We are posting the full details in case they are
useful either way.
Environment
- PLECS version: [ 5.0.4]
- TI C2000 Target Support Package: 2.3.2
- Target: TMS320F28P65x (LAUNCHXL-F28P65X), dual-core
- Tested on hardware with F28P65x only.
Steps to reproduce
We reproduced this with the official demo, without any of our own model
content:
- Open the example model
simple_PIL_model.plecs
(tsp_ti_c2000/demos/simple_PIL_model/). - Remove PIL support (PIL is not supported on multi-CPU targets).
- Move the blink/Digital Output task (which drives GPIO12 in the
unmodified demo) from CPU1 to CPU2. - Build and flash. The LED no longer blinks. Moving the task back to CPU1
makes it work again.
No other changes are needed – a single Digital Output block on a single
pin is enough.
What the generated code looks like
CPU1 file (Controller_CPU0_hal.c) – the core handoff and the pad
configuration are generated:
GPIO_setControllerCore(12, GPIO_CORE_CPU2);
...
GPIO_setPadConfig(12, GPIO_PIN_TYPE_STD);
CPU2 file (Controller_CPU1_hal.c) – this is the only line that
references pin 12 at all:
PLX_DIO_configureOut(DoutHandles[0], 12, &props);
GPIO_setDirectionMode does not appear in either file (0 occurrences in
both). As far as we can tell, PLX_DIO_configureOut()
(tsp_ti_c2000/ccs/28p65x/src/dio_28p65x.c) only sets up register
pointers and writes the initial passive value, and GPIO_setPadConfig()
only controls pull-up / open-drain / invert – neither one sets the
direction.
For comparison, with the same block on CPU1 the generated code does
contain GPIO_setDirectionMode(12, GPIO_DIR_MODE_OUT);.
What we found in the Lua sources (this is where we may be wrong)
We may well be misreading the code generator, so please take this part
with a grain of salt – but this is what led us to suspect a gap rather
than a mistake on our side.
lua/blocks/syscfg.lua appears to be the place where
GPIO_setDirectionMode() is emitted, gated by this condition:
-- GPIO direction may need to be configured by primary or secondary CPU
if (gpio.core == globals.target.getGpioDirConfigCpuNumber(self.cpu)) then
if gpio.cbx_direction.equals('OUTPUT') then
f.PostInitCode:append(
'GPIO_setDirectionMode(%d, GPIO_DIR_MODE_OUT);' % {gpio.unit})
...
The header comment of that same file describes the intended mechanism:
Instances of this module are created implicitly in Coder.lua.
For most targets, a single instance is created to generate initialization
code for the primary CPU.
For some targets, a second instance is created to generate initialization
code for the secondary CPU, e.g. for configuring the direction of GPIO.
In lua/Coder.lua, however, only one syscfg instance seems to be
created for the C28x targets, always for the primary CPU (cpu = 0),
while the F29 (29H85x) family gets one instance per CPU:
if T.targetMatches({'2806x', '2833x', '2837x', '2838x', '28003x',
'28004x', '280013x', '28P55x', '28P65x'}) then
FactoryBlock:makeBlock('syscfg', 0)
elseif T.targetMatches({'29H85x'}) then
for _, cpu in ipairs(Registry.UsedCpus) do
FactoryBlock:makeBlock('syscfg', cpu)
end
...
And getGpioDirConfigCpuNumber() appears to be overridden only in
lua/targets/TI29H85x.lua:
function T.getGpioDirConfigCpuNumber(cpu)
-- on this chip, the GPIO direction is configured by the CPU that owns the GPIO
return T.getTiCpuNumber(cpu)
end
TI28P65x.lua has no such override (it does local T = require('targets.TI28')), so it would fall back to the base
implementation in lua/targets/TI28.lua:
function T.getGpioDirConfigCpuNumber(cpu)
-- default is CPU1
return 1
end
If we read this correctly, that would mean the gating condition always
evaluates to gpio.core == 1 on F28P65x. Our pin is registered with
core = 2 – this is visible in the generated output itself, since the
emitted line GPIO_setControllerCore(12, GPIO_CORE_CPU2) is produced from
the format string 'GPIO_CORE_CPU%(core)d'. With 2 == 1 never being
true, the direction-mode line would never be emitted, which matches what
we observe. It would also explain why the CPU1 case works (core = 1).
This suggests to us that the per-CPU syscfg mechanism described in the
header comment is implemented for F29H85x but not (yet) for the C28x
dual-core devices. But again – we may be misreading how the generator
works.
Questions
- Is a Digital Output block on the secondary CPU a supported
configuration on F28P65x? If not, could the Coder emit a warning or an
error instead of generating silently non-functional code? - If it is supported – is there a setting or an additional block we are
missing that would cause the direction to be configured?
Thanks a lot in advance – and apologies if we have simply overlooked
something obvious.
Update
By using an C-Script one the base task with:
Declation:
#ifdef \__TMS320C28XX_\_
extern void GPIO_setDirectionMode(unsigned long pin, int pinIO);
#endif
Start:
#ifdef \__TMS320C28XX_\_
GPIO_setDirectionMode(12, 1); /* 1 = GPIO_DIR_MODE_OUT */;
#endif
the Blink task on CPU2 works.