Skip to content

Instantly share code, notes, and snippets.

Created March 6, 2024 15:26
Show Gist options
  • Save xypron/43b58aac1232e73b8eff749fbcc3b4b3 to your computer and use it in GitHub Desktop.
Save xypron/43b58aac1232e73b8eff749fbcc3b4b3 to your computer and use it in GitHub Desktop.
OpenOCD specification for PolarFire Icicle Board
# Copyright (C) 2015-2020 by Microchip Technology Inc. *
# *
# *
# This program is free software; you can redistribute it and/or modify *
# it under the terms of the GNU General Public License as published by *
# the Free Software Foundation; either version 2 of the License, or *
# (at your option) any later version. *
# *
# This program is distributed in the hope that it will be useful, *
# but WITHOUT ANY WARRANTY; without even the implied warranty of *
# GNU General Public License for more details. *
# *
# You should have received a copy of the GNU General Public License *
# along with this program. If not, see <>. *
# Microsemi RISC-V
# Device name to device family name lookup table
array set device_family {
AFS090 Fusion
AFS250 Fusion
M1AFS250 Fusion
U1AFS250 Fusion
AFS600 Fusion
M1AFS600 Fusion
P1AFS600 Fusion
U1AFS600 Fusion
AFS1500 Fusion
M1AFS1500 Fusion
P1AFS1500 Fusion
U1AFS1500 Fusion
A2F060 SmartFusion
A2F200 SmartFusion
A2F500 SmartFusion
M2S005 SmartFusion2
M2S010 SmartFusion2
M2S025 SmartFusion2
M2S050 SmartFusion2
M2S060 SmartFusion2
M2S090 SmartFusion2
M2S150 SmartFusion2
A4P200 SmartFusion2
# MPFS PolarFireSoC
# MPFS025 PolarFireSoC
# MPFS095 PolarFireSoC
# MPFS160 PolarFireSoC
# MPFS250 PolarFireSoC
# MPFS460 PolarFireSoC
# Device name to device IDCODEs lookup table
# MPFS devices each have three IDCODEs, one for each type of access -
# system controller, debug and trace
array set device_idcodes {
MPFS "0x0f8581cf 0x0f8781cf 0x0f8771cf 0x0f8181cf 0x0f8381cf 0x0f83c1cf 0x0f8191cf 0x0f8391cf 0x0f83d1cf 0x0f81a1cf 0x0f83a1cf 0x0f83e1cf 0x0f81b1cf 0x0f83b1cf 0x0f83f1cf"
MPFS025 "0x0f8581cf 0x0f8781cf 0x0f8771cf"
MPFS095 "0x0f8181cf 0x0f8381cf 0x0f83c1cf"
MPFS160 "0x0f8191cf 0x0f8391cf 0x0f83d1cf"
MPFS250 "0x0f81a1cf 0x0f83a1cf 0x0f83e1cf"
MPFS460 "0x0f81b1cf 0x0f83b1cf 0x0f83f1cf"
RTPFS "0x0F8991CF 0x0F8B91CF 0x0F8BD1CF 0x0F89B1CF 0x0F8BB1CF 0x0F8BF1CF"
RTPFS160 "0x0F8991CF 0x0F8B91CF 0x0F8BD1CF"
RTPFS460 "0x0F89B1CF 0x0F8BB1CF 0x0F8BF1CF"
# Device name to device eNVM size lookup table
array set device_envm_size {
AFS090 0x40000
AFS250 0x40000
M1AFS250 0x40000
U1AFS250 0x40000
AFS600 0x80000
M1AFS600 0x80000
P1AFS600 0x80000
U1AFS600 0x80000
AFS1500 0x100000
M1AFS1500 0x100000
P1AFS1500 0x100000
U1AFS1500 0x100000
M2GL005 0x20000
M2GL010 0x40000
M2GL025 0x40000
M2GL050 0x40000
M2GL060 0x40000
M2GL090 0x80000
M2GL150 0x80000
A2F060 0x20000
A2F200 0x40000
A2F500 0x80000
M2S005 0x20000
M2S010 0x40000
M2S025 0x40000
M2S050 0x40000
M2S060 0x40000
M2S090 0x80000
M2S150 0x80000
A4P200 0x20000
# What size is MPFS envm?
# MPFS 0x20000
# MPFS025 0x20000
# MPFS095 0x20000
# MPFS160 0x20000
# MPFS250 0x20000
# MPFS460 0x20000
# Device family name to device eNVM driver name lookup table
# Note that microsemi-smartfusion2-envm works for SmartFusion2 and IGLOO2
array set envm_driver {
Fusion microsemi-fusion-coreahbnvm
IGLOO2 microsemi-smartfusion2-envm
SmartFusion microsemi-smartfusion-envm
SmartFusion2 microsemi-smartfusion2-envm
# PolarFireSoC microsemi-polarfire-soc-envm
# Device family name to device eNVM default base address lookup table
# Fusion envm base address arbitrarily defaulted to 0x00000000
array set envm_base {
Fusion 0x00000000
IGLOO2 0x60000000
SmartFusion 0x60000000
SmartFusion2 0x60000000
# PolarFireSoC 0x20200000 or 0x20220000?
# PolarFire SoC (MPFS) hart id to name lookup table
array set mpfs_hart_name {
0 hart0_e51
1 hart1_u54_1
2 hart2_u54_2
3 hart3_u54_3
4 hart4_u54_4
# Process DEVICE variable
if {![exists DEVICE]} {
# Not set so default to the generic "FPGA"
} else {
# Normalise - convert to lowercase
set DEVICE [string toupper $DEVICE]
# PolarFire SoC/MPFS TODOs
# device size (MPFSxxx)
# envm
# soft core Mi-V in PF SoC fabric
# Treat "G5SOC" as legacy synonym for "MPFS"
if {[string range $DEVICE 0 4] eq "G5SOC"} {
# MPFS has one FPGA top level TAP for FPGA access & RISC-V multi-core debug
if {[string range $DEVICE 0 3] eq "MPFS"} {
set DEVICE [string range $DEVICE 0 6]
set FPGA_TAP "N"
# Look up DEVICE envm/flash details
if {[exists device_family($DEVICE)]} {
# if found then default flash parameters unless already set
if {![exists FLASH_DRIVER]} {
set FLASH_DRIVER $envm_driver($device_family($DEVICE))
if {![exists FLASH_BASE]} {
set FLASH_BASE $envm_base($device_family($DEVICE))
if {![exists FLASH_SIZE]} {
set FLASH_SIZE $device_envm_size($DEVICE)
# IRCODE needed to address UJTAG/uj_jtag slave
if {![exists UJ_JTAG_IRCODE]} {
# TODO: validate - e.g. 2 hex digit value
# For PolarFire SoC (MPFS) check COREID, default to -1 (single debug connection
# to all harts) if unspecified or invalid (out of range [-1..4])
if {[string range $DEVICE 0 3] eq "MPFS"} {
if {[exists COREID]} {
if {[expr (($COREID < -1) || ($COREID > 4))]} {
echo [format "Warn : COREID %s not in range (-1..4), defaulting to -1 (single connection to all harts)" $COREID]
set COREID -1
} else {
# echo "Info : COREID not specified so defaulting to -1 (single debug connection to all harts)"
set COREID -1
# Convert DEVICE to lowercase
set DEVICE [string tolower $DEVICE]
if {$FPGA_TAP eq "Y"} {
# FPGA TAP present, occludes RISC-V CPU DTM TAP and must be disabled/bypassed first
# Start off in standard JTAG mode
microsemi_flashpro tunnel_jtag_via_ujtag off
jtag newtap $DEVICE tap -irlen 8 -expected-id 0 -ignore-version
jtag configure $DEVICE.tap -event tap-disable "disable_fpga_tap $DEVICE.tap"
jtag configure $DEVICE.tap -event setup "switch_from_fpga_tap_to_riscv_tap $DEVICE.tap $DEVICE.cpu"
# RISC-V CPU DTM TAP - disabled/not visible by default until FPGA TAP disabled/bypassed
jtag newtap $DEVICE cpu -irlen 5 -expected-id 0x10e31913 -expected-id 0x1c0011cf -disable
jtag configure $DEVICE.cpu -event tap-enable enable_riscv_tap
# TAP management event handlers/procedures
# Switch from FPGA TAP to RISC-V CPU DTM TAP (by tunnelling JTAG via UJTAG)
proc switch_from_fpga_tap_to_riscv_tap {fpga_tap riscv_tap} {
jtag tapdisable $fpga_tap
jtag tapenable $riscv_tap
# Read RISC-V CPU DTM IDCODE as a sanity check
irscan $riscv_tap 0x01
set idcode [string tolower [drscan $riscv_tap 32 0]]
echo [format "Info : RISC-V IDCODE = 0x%s" $idcode]
# Disable/bypass FPGA TAP
proc disable_fpga_tap {fpga_tap} {
# Tell UJTAG/uj_jtag to address RISC-V CPU DTM
irscan $fpga_tap $UJ_JTAG_IRCODE -endstate IRPAUSE
runtest 8
# Enable tunnelled JTAG mode
microsemi_flashpro tunnel_jtag_via_ujtag on
proc enable_riscv_tap {} {
# Nothing to do here - the work is done in disable_fpga_tap
# but the handler still needs to be defined
} else {
# "Direct" debugging not via FPGA TAP
# Select appropriate irlen and expected idcodes
if {[string range $DEVICE 0 3] eq "mpfs"} {
set IRLEN 8
set EXPECTED_IDS "-expected-id 0x0f8581cf -expected-id 0x0f8781cf -expected-id 0x0f8771cf -expected-id 0x0f8181cf -expected-id 0x0f8381cf -expected-id 0x0f83c1cf -expected-id 0x0f8191cf -expected-id 0x0f8391cf -expected-id 0x0f83d1cf -expected-id 0x0f81a1cf -expected-id 0x0f83a1cf -expected-id 0x0f83e1cf -expected-id 0x0f81b1cf -expected-id 0x0f83b1cf -expected-id 0x0f83f1cf"
} else {
set IRLEN 5
set EXPECTED_IDS "-expected-id 0x10e31913 -expected-id 0x1c0011cf -expected-id 0x1e50105f -ignore-version"
eval jtag newtap $DEVICE cpu -irlen $IRLEN $EXPECTED_IDS
# Target CPU
if {[string range $DEVICE 0 3] eq "mpfs"} {
# PolarFire SoC (MPFS)
if {$COREID eq -1} {
# Single debug connection to all harts
set _TARGETNAME_0 $DEVICE.$mpfs_hart_name(0)
set _TARGETNAME_1 $DEVICE.$mpfs_hart_name(1)
set _TARGETNAME_2 $DEVICE.$mpfs_hart_name(2)
set _TARGETNAME_3 $DEVICE.$mpfs_hart_name(3)
set _TARGETNAME_4 $DEVICE.$mpfs_hart_name(4)
target create $_TARGETNAME_0 riscv -chain-position $DEVICE.cpu -coreid 0 -rtos hwthread
target create $_TARGETNAME_1 riscv -chain-position $DEVICE.cpu -coreid 1
target create $_TARGETNAME_2 riscv -chain-position $DEVICE.cpu -coreid 2
target create $_TARGETNAME_3 riscv -chain-position $DEVICE.cpu -coreid 3
target create $_TARGETNAME_4 riscv -chain-position $DEVICE.cpu -coreid 4
$_TARGETNAME_1 configure -event reset-init init_regs
$_TARGETNAME_2 configure -event reset-init init_regs
$_TARGETNAME_3 configure -event reset-init init_regs
$_TARGETNAME_4 configure -event reset-init init_regs
} else {
# Debug connection to a specific hart
set _TARGETNAME_0 $DEVICE.$mpfs_hart_name($COREID)
target create $_TARGETNAME_0 riscv -chain-position $DEVICE.cpu -coreid $COREID
} else {
# Mi-V soft core - implicitly single hart
target create $_TARGETNAME_0 riscv -chain-position $DEVICE.cpu
$_TARGETNAME_0 configure -event reset-init board_reset_init
$_TARGETNAME_0 configure -event reset-init init_regs
# Flash
# Flash may be defined via parameters:
# FLASH_DRIVER: cfi or microsemi-fusion-coreahbnvm (for Fusion/AFSXXX) or
# microsemi-smartfusion2-envm (for SmartFusion2/M2SXXX) or
# microsemi-smartfusion-envm (for SmartFusion/A2FXXX) or
# microsemi-polarfire-soc-envm (for PolarFire SoC/MPFSXXX)
# FLASH_BASE - e.g. 0x10000000
# FLASH_SIZE - e.g. 0x8000
# FLASH_WIDTH - e.g. 0, 1, 2 or 4 or not defined (defaults to 0)
# If FLASH_DRIVER is defined then the other parameters are assumed to be defined
# and to be valid
if {[exists FLASH_DRIVER]} {
# Reset configuration
# Only TRSTn supported
reset_config trst_only
# Utility procedures
proc board_reset_init {} {
# call board level reset-init if defined
if {[exists -proc do_board_reset_init]} {
proc init_regs {} {
reg pc 0
# gdb-detach event handler
$_TARGETNAME_0 configure -event gdb-detach {
# resume execution on debugger detach
# TODO: why does this eliminate the second (redundant?) probe/examine?
proc init_reset {mode} {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment