The RA6M4 supports multiple clock sources from external crystals to on-chip oscillators. By default, the EK-RA6M4 board in the Zephyr tree is configured to use an external crystal and, in this blog, post I’ll describe how to use the high-speed on-chip oscillator (HOCO) instead.

The clock source used by the RA6M4 on the EK-RA6M4 is defined in the Board Device Tree Defintion. In this file the external 24MHz crystal is selected as follows:

&xtal {
	clock-frequency = <DT_FREQ_M(24)>;
	mosel = <0>;
	#clock-cells = <0>;
	status = "okay";
};

This XTAL clock source is then used as the input to the on-chip PLL:

&pll {
	clocks = <&xtal>;
	div = <3>;
	mul = <25 0>;
	status = "okay";
};

The EK-RA6M4 board uses the ra6m4af3cfb variant of the RA6M4 family. We can view the available clock sources by looking at the device tree for this SoC and can see it contains the following clock nodes:

xtal: clock-main-osc {
	compatible = "renesas,ra-cgc-external-clock";
	clock-frequency = <DT_FREQ_M(24)>;
	#clock-cells = <0>;
	status = "disabled";
};

hoco: clock-hoco {
	compatible = "fixed-clock";
	clock-frequency = <DT_FREQ_M(20)>;
	#clock-cells = <0>;
};

moco: clock-moco {
	compatible = "fixed-clock";
	clock-frequency = <DT_FREQ_M(8)>;
	#clock-cells = <0>;
};

loco: clock-loco {
	compatible = "fixed-clock";
	clock-frequency = <32768>;
	#clock-cells = <0>;
};

subclk: clock-subclk {
	compatible = "renesas,ra-cgc-subclk";
	clock-frequency = <32768>;
	#clock-cells = <0>;
	status = "disabled";
};

From this we can see that the XTAL clock is disabled by default and the HOCO is enabled by default.

So, to switch from using the XTAL to using the HOCO we should just be able to go back to the EK-RA6M4 board device tree definition, remove the XTAL node and update the PLL source clock to the HOCO clock. Note that we don’t need to explicitly enable the HOCO clock as it is enabled by default in the SoC device tree.

//&xtal {
//	clock-frequency = <DT_FREQ_M(24)>;
//	mosel = <0>;
//	#clock-cells = <0>;
//	status = "okay";
//};

&pll {
	clocks = <&hoco>;
	div = <3>;
	mul = <25 0>;
	status = "okay";
};

Unfortunately after making this change we get an error when trying to build a simple example (blinky):

/zephyrproject/zephyr/include/zephyr/toolchain/gcc.h:87:36: error: static assertion failed: "CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC must match the configuration of the clock supplying the CPU "
   87 | #define BUILD_ASSERT(EXPR, MSG...) _Static_assert((EXPR), "" MSG)
	  |                                    ^~~~~~~~~~~~~~
/zephyrproject/zephyr/drivers/clock_control/clock_control_renesas_ra_cgc.c:36:1: note: in expansion of macro 'BUILD_ASSERT'
   36 | BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == SYS_CLOCK_HZ,
	  | ^~~~~~~~~~~~

This error is generated by the following code in the clock_control_renesas_ra_cgc.c file:

BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == SYS_CLOCK_HZ,
	     "CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC must match the configuration of the clock "
	     "supplying the CPU ");

So what’s going on here…

The CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC is a runtime constant that “represents the fastest cycle counter that the OS is able to present to the user”. From the SoC kconfig file we see that the value of CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC is generated at build time as follows:

DT_ICLK_PATH := $(dt_nodelabel_path,iclk)

config SYS_CLOCK_HW_CYCLES_PER_SEC
	default $(dt_node_int_prop_int,$(DT_ICLK_PATH),clock-frequency)

What this is saying is that SYS_CLOCK_HW_CYCLES_PER_SEC has the clock-frequency value specified in the iclk node. The iclk node is defined as follows in the SoC device tree:

iclk: iclk {
	compatible = "renesas,ra-cgc-pclk";
	clock-frequency = <200000000>;
	div = <1>;
	#clock-cells = <2>;
	status = "okay";
};

So by default CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC has a value of 20000000 (200MHz). Note, we can confirm this by looking at the build/zephyr/.config file.

Now lets figure out how the value of SYS_CLOCK_HZ is calculated…

SYS_CLOCK_HZ is generated in clock_control_renesas_ra_cgc.c using the following formula:

#define sys_clk DT_NODELABEL(iclk)

#define SYS_CLOCK_HZ (BSP_STARTUP_SOURCE_CLOCK_HZ / DT_PROP(sys_clk, div))

The value of BSP_STARTUP_SOURCE_CLOCK_HZ is defined in bsp_clocks.h as follows:

#define BSP_STARTUP_SOURCE_CLOCK_HZ    (((BSP_PRV_PLL_SOURCE_FREQ_HZ * (BSP_CFG_PLL_MUL + 1U)) >> 1) / \
 										  (BSP_CFG_PLL_DIV + 1U))

OK now we are getting somewhere.

We know that the values of CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC and SYS_CLOCK_HZ must match. We also know that the value of CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC is 200000000 and now we know that SYS_CLOCK_HZ is calculated based on the PLL parameters.

So, all we have to do is update the PLL to give us a 200MHz output from a 20MHz (HOCO) input, which can be acheived by modifying the pll node in the EK-RA6M4 device tree as follows:

&pll {
	clocks = <&hoco>;
	div = <1>;
	mul = <10 0>;
	status = "okay";
};

In summary, to switch the RA6M4 found on the EK-RA6M4 to use the HOCO instead of the XTAL a few minor modifications are required to the board device tree definition:

//&xtal {
//  clock-frequency = <DT_FREQ_M(24)>;
//  mosel = <0>;
//  #clock-cells = <0>;
//  status = "okay";
//};

&subclk {
	status = "okay";
};

//&pll {
//  clocks = <&hoco>;
//  div = <3>;
//  mul = <25 0>;
//  status = "okay";
//};

&pll {
	clocks = <&hoco>;
	div = <1>;
	mul = <10 0>;
	status = "okay";
};

Hopefully someone finds this useful, stay tuned for further posts regarding the use of Zephyr on RA Microcontrollers!