![Article Title](https://tistory1.daumcdn.net/tistory/342254/skin/images/icon_post_title.gif)
%% LeCroy Exerciser Script - For NVMe Emulation
* Reference : PETrainerScriptManual_v*.pdf
http://teledynelecroy.com/protocolanalyzer/pci-express/summit-z3-16-exerciser-with-smbus-support
* LeCroy Exerciser Script
☞ http://ya-n-ds.tistory.com/3108 : PCIe Commands
☞ http://ya-n-ds.tistory.com/3116 : NVMe (1) Preparation for IO
☞ http://ya-n-ds.tistory.com/3131 : NVMe (2) IO Command ( + MCTP, Completion options )
< Step 0. Option Preparation for NVMe Host Emulation >
# Exerciser BAR Menu : Setup -> Generation Options -> Transactions
- Host Memory Regions : Mem_64, Address 0x4_2FAA_8000, Length 0x1_0000 ( 4KB Blocks )
< Step 1. PCIe Configuration >
Config=Definitions
{
HOST_REQUESTER_ID = (1:0:0)
DEVICE_ID = (2:1:0)
BAR0_ADDRESS = 0xEC03_0000 ;; Little Endian
BAR0_ADDRESS_BIG_END = 0x0000_003EC
BAR2_ADDRESS_BIG_END = 0x0000_002EC ;; Little Endian = 0xEC02_0000
PXCAP_BASE_ADDR = 0x0000_0070 ;; PCIe Capability Base Address ( Device HW dependency )
PXCAP_PXDC_OFFSET = 0x0000_0008 ;; PCIe Device Congrol register offset
MSI_X_TABLE_OFFSET = 0x0000_4000 ;; Device HW dependency, This can be read from MSIXCP.MTAB register
MSI_X_TABLE_BASE_ADDR = ( BAR0_ADDRESS + MSI_X_TABLE_OFFSET )
MSI_X_CAP_BASE_ADDR = 0xB0 ;; Device HW dependency
HOST_MSIX_INTR0_BASE_ADDR_BIG_END = 0x0000_E0FE ;; MSI-X Interrupt0 BA in Host, Littel Endian = 0xFEE0_0000
HOST_MSIX_INTR1_BASE_ADDR_BIG_END = 0x0000_E2FE ;; MSI-X Interrupt1 BA in Host, Littel Endian = 0xFEE2_0000
HOST_MSIX_INTR0_MSG_DATA_BIG_END = 0xB040_0000 ;; MSI-X Interrupt0 Message Data, Littel Endian = 0x0000_40B0
HOST_MSIX_INTR1_MSG_DATA_BIG_END = 0xA040_0000 ;; MSI-X Interrupt1 Message Data, Littel Endian = 0x0000_40A0
}
template = TLP
{
Name = "Temp_ConfigWrite0"
TLPType=CfgWr0
Length = 1
RequesterId = HOST_REQUESTER_ID
FirstDwBe = 0xF
DeviceId = DEVICE_ID
}
template = TLP
{
Name = "Temp_ConfigRead0"
TLPType=CfgRd0
Length = 1
RequesterId = HOST_REQUESTER_ID
FirstDwBe = 0xF
DeviceId = DEVICE_ID
}
template = TLP
{
Name = "Temp_MWr32_OneDword"
TLPType = MWr32
Length = 1
RequesterId = HOST_REQUESTER_ID
FirstDwBe = 0xF
}
template = TLP
{
Name = "Temp_MRd32_OneDword"
TLPType = MRd32
Length = 1
RequesterId = HOST_REQUESTER_ID
FirstDwBe = 0xF
}
< Step 1. PCIe Preparation for NVMe >
packet="Temp_ConfigWrite0"
{
Register = 0x10
Payload = ( BAR0_ADDRESS_BIG_END ) ; Payload is in Big-endian
}
;;wait=TLP { TLPType = Cpl } ;; Wait Completion before Next Command
packet="Temp_ConfigWrite0"
{
Register = 0x14 ;; BAR0 Upper Address for 64bit access
Payload = ( 0 )
}
packet="Temp_ConfigWrite0"
{
Register = 0x18
Payload = ( BAR2_ADDRESS_BIG_END )
}
packet="Temp_ConfigWrite0"
{
Register = 0x1C ;; BAR2 Upper Address for 64bit access
Payload = ( 0 )
}
packet="Temp_ConfigWrite0"
{
FirstDwBe = 0x3
Register = 0x4
Payload = ( 0x46050000 )
;; 2nd Byte : bit[10] Interrupt Disable(Pin-based), bit[8] SERR# Enalbe,
;; 1st Byte : bit[6] Parity Error Enable, bit[2] BME, bit[1]MSE
}
Packet=TLP
{
PSN = 60
TLPType = CfgWr0
FirstDwBe = 0x3
Register = ( PXCAP_BASE_ADDR + PXCAP_PXDC_OFFSET )
Payload = 0x30280000 ;; MPS='0x1'(256B), ERO='1', MRRS='0x2'(512B), ENS='1'
;; 1st Byte : bit[7:5] Max Payload size, bit[4] Enhanced Relaxed Ordering(ERO)
;; 2nd Byte : bit[14:12] Max Read Request size, bit[11] Enable No Snoop
}
;; Set MSI-X Table Entries
;; Vector 0
packet="Temp_MWr32_OneDword"
{
Address = MSI_X_TABLE_BASE_ADDR
Payload = ( HOST_MSIX_INTR0_BASE_ADDR_BIG_END ) ;; Lower Address
}
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( MSI_X_TABLE_BASE_ADDR + 4 )
Payload = ( 0 ) ;; Upper Address
}
packet="Temp_MWr32_OneDword"
{
Tag = 2
Address = ( MSI_X_TABLE_BASE_ADDR + 8 )
Payload = ( HOST_MSIX_INTR0_MSG_DATA_BIG_END )
}
packet="Temp_MWr32_OneDword"
{
Tag = 3
Address = ( MSI_X_TABLE_BASE_ADDR + 0xC )
Payload = ( 0 ) ;; Vector Control - bit[0] Vector Mask bit
}
;; Vector 1
packet="Temp_MWr32_OneDword"
{
Address = ( MSI_X_TABLE_BASE_ADDR + 0x10 )
Payload = ( HOST_MSIX_INTR1_BASE_ADDR_BIG_END )
}
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( MSI_X_TABLE_BASE_ADDR + 0x14 )
Payload = ( 0 ) ;
}
packet="Temp_MWr32_OneDword"
{
Tag = 2
Address = ( MSI_X_TABLE_BASE_ADDR + 0x18 )
Payload = ( HOST_MSIX_INTR1_MSG_DATA_BIG_END )
}
packet="Temp_MWr32_OneDword"
{
Tag = 3
Address = ( MSI_X_TABLE_BASE_ADDR + 0x1C )
Payload = ( 0 )
}
;; Enable MSI-X
packet="Temp_MWr32_OneDword"
{
Register = MSI_X_CAP_BASE_ADDR
FirstDwBe = 0x8 ; Last byte only
Payload = ( 0x11001F80 )
;; Capability ID[7:0]=11h, Next Cap ID[15:8]=00h
;; Message Control : Table Size bit[26:16](RO), Function Mask[30], MSI-X Enable[31]
}
packet="Temp_ConfigRead0"
{
Register = 0x8 ;; Rev.ID, Class Code
}
packet="Temp_ConfigRead0"
{
Register = 0x10 ;; BAR0
}
< Step 2. Initialization for NVMe >
;; Set CQ, SQ Entry Size
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( CONTROLLER_REGISTERS_BASE + 0x14 ) ;; CC(Controller Configuration)
Payload = ( 0x00004600 ) ;; 0x0046_0000
;; bit[23:20] - I/O Completion Queue Entry Size // 2^n ea - 2^4=16ea
;; bit[19:16] - I/O Submission Queue Entry Size // 2^n ea - 2^6=64ea
}
;; Poll for the Ready to go to zero
Loop=Begin { Count=50000 }
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x1C ) ;; CSTS(Controller Status)
StoreData = ( FROM_MEM64, 0 )
;; Store the data returned by Memory Read TLP into Mem64 HostMemRegion with offset=0
}
Loop=Break ;; Break from Loop when the condition is met
{
Location=Mem64
Offset = 0
FieldSize = Dword
Endian = Little
FieldAction = MaskAND
Mask = 0x1
Result = 0 ;; Check bit[0] Ready bit is '0'
}
Loop=End
;; Set ACQS and ASQS in AQA(Admin Queue Attributes)
packet="Temp_OneDwordWrite"
{
Tag = 0
Address = ( CONTROLLER_REGISTERS_BASE + 0x24 )
Payload = ( 0x7F00_7F00 ) ;; 128ea
; bit[27:16] ACQS(Admin Completion Queue Size)
; bit[11:0] ASQS(Admin Completion Queue Size)
}
;; Set Admin Submission Queue address base - ASQ
;; This address corresponds to the base address set for Mem_64 Host region
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( CONTROLLER_REGISTERS_BASE + 0x28 ) ;; Lower Address
Payload = ( 0x0080AA2F ) ;; 0x2FAA_8000
}
packet="Temp_MWr32_OneDword"
{
Tag = 2
Address = ( CONTROLLER_REGISTERS_BASE + 0x2C ) ;; Upper Address
Payload = ( 0x04000000 ) ;; 0x0000_0004
}
;; Set Admin completion Queue address base - ACQ
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( CONTROLLER_REGISTERS_BASE + 0x30 ) ;; Lower Addrss
Payload = ( 0x00A0AA2F ) ;; 0x2FAA_A000
}
packet="Temp_MWr32_OneDword"
{
Tag = 2
Address = ( CONTROLLER_REGISTERS_BASE + 0x34 ) ;; Upper Address
Payload = ( 0x04000000 ) ;; 0x0000_0004
}
;; Disable NVM by writing to the CC register
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( CONTROLLER_REGISTERS_BASE + 0x14 )
Payload = ( 0x00004600 ) ;; 0x0046_0000 bit[0] CC.EN=0
}
wait=1000000
;; Enable NVM by writing to the CC register
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( CONTROLLER_REGISTERS_BASE + 0x14 )
Payload = ( 01004600 ) ;; 0x0046_0001 bit[0] CC.EN=1
}
;; Poll for the Ready status : CC.EN='1' -> Ready='1'
Loop=Begin { Count=50000 }
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x1C )
StoreData = ( FROM_MEM64, 0 )
}
Loop=Break
{
Location=Mem64
Offset = 0 ;
FieldSize = Dword
Endian = Little
FieldAction = MaskAND
Mask = 0x1
Result = 0x1
}
wait=200000 ;; 200us
Loop=End
< Step 3. Queue Creation for NVMe IO >
;; Read the config registers needed for automatic NVMe decoding of the trace
packet="Temp_ConfigRead0"
{
Register = 0x8 ;; Revision ID, Class Code
}
wait=TLP { TLPType = CplD }
packet="Temp_ConfigRead0"
{
Register = 0x10 ;; BAR0
}
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x24 ) ;; AQA - ACQ Size, ASQ Size
}
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x28 ) ;; ASQ Base Address - Lower
}
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x2C ) ;; ASQ Base Address - Upper
}
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x30 ) ;; ACQ Base Address - Lower
}
packet="Temp_MRd32_OneDword"
{
Address = ( CONTROLLER_REGISTERS_BASE + 0x34 ) ;; ACQ Base Address - Upper
}
;; Pre-program command in the Host Memory Region. This is the Mem_64 Host region
;; defined in the generation options file "nvme_host_gen_options.gen"
Structure=NVMe
{
Location=Mem64 ;; Mem_64, Address 0x4_2FAA_8000
Offset = 0
NVMeStructType=AdminCommand
OpcodeAdmin = ADMIN_CREATE_CQ
CID = 0 ;; Command Identifier
PRP1_Low = 0x2FAC8000
PRP1_High = 0x4
Queue_ID = 1
QueueSize = IO_QUEUE_SIZE
PhysContig = Yes
IntEnable = Yes
IntVector = 1
}
Structure=NVMe
{
Location=Mem64
Offset = 64
NVMeStructType=AdminCommand
OpcodeAdmin = ADMIN_CREATE_SQ
CID = 1
Queue_ID = 1
QueueSize = IO_QUEUE_SIZE
PhysContig = Yes
CQID = 1
QPriority = Urgent
PRP1_Low = 0x2FAB8000
PRP1_High = 0x4
}
;; Write Admin Submission Queue Tail Doorbell - 1st
packet="Temp_MWr32_OneDword"
{
Tag = 0
Address = ( CONTROLLER_REGISTERS_BASE + 0x1000 ) ;; Admin SQ Doorbell
Payload = ( 0x01000000 ) ;; 0x0000_0001
}
wait=TLP
{
TLPType = MWr32
Address = HOST_MSIX_INTR0_BASE_ADDR ;; 0xFEE0_0000 - for Admin CQ
}
;; Write Admin Completion Queue Head Doorbell - 1st
packet="Temp_MWr32_OneDword"
{
Tag = 1
Address = ( CONTROLLER_REGISTERS_BASE + 0x1004 ) ;; Admin CQ Doorbell
Payload = ( 0x1000000 ) ;; 0x0000_0001
}
;; Write Admin Submission Queue Tail Doorbell - 2nd
packet="Temp_MWr32_OneDword"
{
Tag = 2
Address = ( CONTROLLER_REGISTERS_BASE + 0x1000 )
Payload = ( 0x02000000 ) ;; 0x0000_0002
}
wait=TLP
{
TLPType = MWr32
Address = HOST_MSIX_INTR0_BASE_ADDR ;; 0xFEE0_0000 - for Admin CQ
}
;; Write Admin Completion Queue Head Doorbell - 2nd
packet="Temp_MWr32_OneDword"
{
Tag = 3
Address = ( CONTROLLER_REGISTERS_BASE + 0x1004 )
Payload = ( 0x02000000 ) ;; 0x0000_0002
}