ThrowTheSwitch / Ceedling

Ruby-based unit testing and build system for C projects
http://throwtheswitch.org
Other
585 stars 245 forks source link

unity_config.h -> UnityHelper.c is not compiled #619

Open ErdlingProductions opened 3 years ago

ErdlingProductions commented 3 years ago

Hello, I am doing this tutorial for cross compilers: http://www.throwtheswitch.org/build/cross

It already works, but with workarounds. A short summary of what I am doing:

My issue: When I follow the tutorial, the "UnityHelper.c" file is not compiled.

This is my file tree:

test
├── simulation
│   ├── sim_instructions.txt
│   └── sim_test_fixture.rb
├── support
│   ├── unity_config.h
│   └── UnityHelper.c
└── test_reg.c

unity_config.h according to tutorial:

#ifndef UNITY_CONFIG_H
#define UNITY_CONFIG_H

#include "unity.h"

void UnityHelperDeadLoop(void);

//Put our exit breakpoint in a place where it won't get sat on: an unused interrupt vector
#define UNITY_OUTPUT_COMPLETE() UnityHelperDeadLoop()

#endif

UnityHelper.c according to tutorial:

void UnityHelperDeadLoop(void) __attribute__((address(0x0280)));
void UnityHelperDeadLoop(void)
{
    while(1) {};
}

When I try to run ceedling test:all, my compiler complains, that it has an undefined symbol _UnityHelperDeadLoop which means, that UnityHelper.c was not compiled.

My workaround was to create a UnityHelper.h file and put the UnityHelperDeadLoop() declaration there and include that header in my test_reg.c file. However this is not desirable, as I would have to include it manually.

This is my project.yml:

:project:
  :use_exceptions: FALSE
  :use_test_preprocessor: TRUE
  :use_auxiliary_dependencies: TRUE
  :build_root: build
  :release_build: TRUE
  :test_file_prefix: test_

:release_build:
 :output: MyPicApp.out
 :use_assembly: FALSE

:environment:

:extension:
  :executable: .out
  :object: .p1

:paths:
  :test:
    - +:test/**
    - -:test/support
  :source:
    - src/**
  :support:
    - test/support
  :include:
    - "/opt/microchip/xc8/v2.10/pic/include"

:defines:
  # in order to add common defines:
  #  1) remove the trailing [] from the :common: section
  #  2) add entries to the :common: section (e.g. :test: has TEST defined)
  :commmon: &common_defines
    - __18F25K20__
    - UNITY_INT_WIDTH=16
    - CMOCK_MEM_INDEX_TYPE=uint16_t
    - CMOCK_MEM_PTR_AS_INT=uint16_t
    - CMOCK_MEM_ALIGN=1
    - CMOCK_MEM_SIZE=256
  :test:
    - *common_defines
    - UNITY_INCLUDE_CONFIG_H
    - TEST
  :test_preprocess:
    - *common_defines
    - TEST

:cmock:
  :mock_prefix: mock_
  :when_no_prototypes: :warn
  :enforce_strict_ordering: TRUE
  :plugins:
    - :ignore
    - :callback
  :treat_as:
    uint8:    HEX8
    uint16:   HEX16
    uint32:   UINT32
    int8:     INT8
    bool:     UINT8

:tools:
  :test_compiler:
    :executable: xc8-cc
    :arguments:
      - -mcpu=18F25K20
      - -c
      - "${1}"
      - -o "${2}"
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
#- -DUNITY_INCLUDE_CONFIG_H
  :test_linker:
    :executable: xc8-cc
    :arguments:
      - -mcpu=18F25K20
      - ${1}
      - -o "./build/test/test_build.out"
      - -Wl,-Map=./build/release/TestOutput.map,--report-mem
  :test_fixture:
    :executable: ruby
    :name: "Microchip simulator test fixture"
    :stderr_redirect: :win #inform Ceedling what model of $stderr capture to use
    :arguments:
      - test/simulation/sim_test_fixture.rb

  :release_compiler:
    :executable: xc8-cc
    :arguments:
      - -mcpu=18F25K20
      - -c
      - "${1}"
      - -o "${2}"
      - -I"$": COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE
      - -D$: COLLECTION_DEFINES_RELEASE_AND_VENDOR
  :release_linker:
    :executable: xc8-cc
    :arguments:
      - -mcpu=18F25K20
      - ${1}
      - -o "${2}"
      - -Wl,-Map=./build/release/MyPicApp.map,--report-mem

:plugins:
  :load_paths:
#- vendor/ceedling/plugins
  :enabled:
    - stdout_pretty_tests_report
    - module_generator

I can't find my (thinking) error, any help would be greatly appreciated. Thanks a lot in advance and best regards!

JuPrgn commented 3 years ago

Hi,

I am using Ceedling with a dsPIC33 I can share some of my config files. I do not use this dead loop helper.

test ├── simulation │ ├── sim_instructions.txt │ └── sim_test_fixture.rb ├── support │ ├── xc.h │ └── and some other customs .h └── test_files.c

mdb_instructions.txt

Device dsPIC33EP256MC506
Hwtool SIM
set oscillator.frequency 60
set oscillator.frequencyunit Mega
set oscillator.rcfrequency 250
set oscillator.rcfrequencyunit Kilo
Set uart1io.uartioenabled true
Set uart1io.output file
Set uart1io.outputfile "test/simulation/out.txt"
Program "build/release/TestBuild.out"
Run
Wait
Quit

sim_test_fixture.rb

require 'rbconfig'
is_windows = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)

OUT_FILE = "test/simulation/out.txt"
File.delete OUT_FILE if File.exists? OUT_FILE 

sim_io = IO.popen('"C:\Program Files\Microchip\MPLABX\v5.45\mplab_platform\bin\mdb.bat" test/simulation/mdb_instructions.txt')

Process.wait(sim_io.pid)

if File.exists? OUT_FILE 
    file_contents = File.read OUT_FILE
    print file_contents
end

My project.yml is similar to your but I have those specific things :

:environment:
  - :mcpu: 33EP256MC506
  - :XC16PATH: "C:/Program Files/Microchip/xc16/v1.70"
  - :XC16GCC: "#{ENV['XC16PATH']}/bin/xc16-gcc.exe"
  - :XC16P1: "#{ENV['XC16PATH']}/support/dsPIC33E/h"
  - :XC16P2: "#{ENV['XC16PATH']}/support/generic/h"
  - :XC16P3: "#{ENV['XC16PATH']}/include/lega-c"

:paths:
  :include:
    - "#{ENV['XC16P1']}"
    - "#{ENV['XC16P2']}"
    - "#{ENV['XC16P3']}"

:tools:
  :test_dependencies_generator:
    :executable: "#{ENV['XC16GCC']}"
    :name: 'XC 16 Test Dependencies Generator'
    :arguments:
      - -E
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -D$: DEFINES_TEST_PREPROCESS
      - -DGNU_COMPILER
      - -MT "${3}"
      - -MM
      - -MD
      - -MG
      - -MF"${2}"
      - -c "${1}"
  :test_includes_preprocessor:
    :executable: "#{ENV['XC16GCC']}"
    :name: 'XC 16 Test Include Preprocessor'
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -E
      - -MM
      - -MG
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -D$: DEFINES_TEST_PREPROCESS
      - -DGNU_COMPILER
      - "${1}"
  :test_file_preprocessor:
    :executable: "#{ENV['XC16GCC']}"
    :name: 'XC 16 Test File Preprocessor'
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -E
      - -DGNU_COMPILER
      - "${1}"
      - -o "${2}"
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -D$: DEFINES_TEST_PREPROCESS
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
  :test_file_preprocessor_directives:
    :executable: "#{ENV['XC16GCC']}"
    :name: 'XC 16 Test File Preprocessor Directives'
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -E
      - -DGNU_COMPILER
      - "${1}"
      - -o "${2}"
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -D$: DEFINES_TEST_PREPROCESS
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
      - -fdirectives-only
  :test_compiler:
    :executable: "#{ENV['XC16GCC']}"
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -x c
      - -omf=elf
      - -msmart-io=1
      - -mno-eds-warn  
      - -msfr-warn=off
      - -legacy-libc
      - -c
      - "${1}"
      - -o "${2}"
      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
      - -Wall
      - -mlarge-code
      - -mlarge-data
      - -mlarge-scalar
      - -mconst-in-data
      - -std=gnu99
  :test_linker:
    :executable: "#{ENV['XC16GCC']}"
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -omf=elf 
      - -DXPRJ_default=default  
      - -legacy-libc 
      - ${1}
      - -o "./build/release/TestBuild.out"
      - -Wl,--allow-multiple-definition,-Tp#{ENV['MCPU']}.gld,--heap=0,--stack=16,--local-stack,--check-sections,--data-init,--pack-data,--handles,--isr,--no-gc-sections,--fill-upper=0,--stackguard=16,--no-force-link,--smart-io,-Map=./build/release/TestOutput.map,--report-mem,--memorysummary,./build/release/memoryfile.xml
  :test_fixture:
    :executable: ruby
    :name: "Microchip simulator test fixture"
    :stderr_redirect: :win #inform Ceedling what model of $stderr capture to use
    :arguments:
      - test/simulation/sim_test_fixture.rb
  :release_compiler:
    :executable: "#{ENV['XC16GCC']}"
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -x c
      - -omf=hex
      - -msmart-io=1
      - -mno-eds-warn  
      - -msfr-warn=off
      - -legacy-libc
      - -c
      - "${1}"
      - -o "${2}"
      - -I"$": COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR
      - -I"$": COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE
      - -D$: COLLECTION_DEFINES_RELEASE_AND_VENDOR
      - -Wall
      - -mlarge-code
      - -mlarge-data
      - -mlarge-scalar
      - -mconst-in-data
  :release_linker:
    :executable: "#{ENV['XC16GCC']}"
    :arguments:
      - -mcpu=#{ENV['MCPU']}
      - -legacy-libc
      - ${1}
      - -o "${2}"
      - -Wl,-Tp#{ENV['MCPU']}.gld,--heap=0,--stack=16,--local-stack,--check-sections,--data-init,--pack-data,--handles,--isr,--no-gc-sections,--fill-upper=0,--stackguard=16,--no-force-link,--smart-io,-Map=./build/release/ReleaseOutput.map,--report-mem,--memorysummary,./build/release/memoryfile.xml
ErdlingProductions commented 3 years ago

Hello @JuPrgn, thanks for your reply.

How do you exit the simulator, after the tests are done? If I do it like you, with e.g. a wait 30000 command, the simulator keeps running for those 30s. The program however exits (main() returns) and, just like in a real PIC, starts the program over and executes the tests again and again. The deadloop is there to create a function at a fixed address, at which the simulator breaks the execution and then ends itself. I don't think the contents of this functions matter, the address is if importance. The deadloop is more of a fallback (my guess).

Otherwise our setup looks very similar to me, mine is smaller and I use Linux.

Maybe one tip for you: you do not need to place the <xc.h> file in the support folder. If you check my project.yml, you can put its actual path under :include.

An explanation to how to get the UnityHelper.c file to compile, as described in the tutorial, is my primary goal (I think :) :)

Best regards

JuPrgn commented 3 years ago

I do not need anything else than mdb_instructions.txt wait and quit to exit the simulator. I don't get why you have this infinite loop. Maybe this could be a simulator difference using linux ?

This is a dummy xc.h that let me customize some builtin functions.

JuPrgn commented 3 years ago

Please note you do not need to specify a wait time.

ErdlingProductions commented 3 years ago

Hello @JuPrgn, I am back from vacation ;) Thanks for your input so far.

I tried your config and ran into the same issue as mentioned. After the main()-function in the runner file returns, the main()-function is executed again. This loops forever. I can verify this by looking at the generated out.txt, I see the same tests being executed over and over.

The infinite helper loop is placed inside a function at a specific address in flash and in the simulator config, a breakpoint is set for that address. Ceedling has a Macro called UNITY_OUTPUT_COMPLETE, to which a function can be assigned: #define UNITY_OUTPUT_COMPLETE() UnityHelperDeadLoop(). So when the main()-function returns, this function is called (I presume, that this is what happens). This triggers the breakpoint, and the simulator exits (the endless loop is just safety I think, you could probably as well use Nop()s or nothing at all). And for the wait time: If I use your example, this just means, that it never stops.

Maybe it is a OS difference in the simulator, or maybe it is a difference in the compiler? I use XC8, you use XC16. I like your approach way better, because it's so much simpler and cleaner.