当前位置:网站首页>[detailed explanation of AUTOSAR 14 startup process]
[detailed explanation of AUTOSAR 14 startup process]
2022-07-05 18:43:00 【Ke Yuqian】
Autosar fourteen Start up process details

Link to the file :
1. vLinkGen_Template.lsl
It is noted here that the starting function is :brsStartupEntry function ;
"StartupEntry" = "brsStartupEntry";
"_start_tc0_asr" = "brsStartupEntry";
"_start_tc1_asr" = "brsStartupEntry";
"_start_tc2_asr" = "brsStartupEntry";
"_start_tc3_asr" = "brsStartupEntry";
"_start_tc4_asr" = "brsStartupEntry";
"_start_tc5_asr" = "brsStartupEntry";
2. BrsHwStartup.c
brsStartupEntry Function as follows , Next, let's talk about what this function does :
2.1 The configured memory area belongs to 0
#if defined (VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS)
# if (VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS>1uL)
/* Loop over all entries of vLinkGen_ZeroInit_Early_Groups and zero the configured memory areas. */
volatile uint32 *memPtr2;
uint8 areaNum;
for(areaNum = 0; areaNum < vLinkGen_ZeroInit_Early_GroupsSet.Num; areaNum++)
{
if(vLinkGen_ZeroInit_Early_GroupsSet.Areas[areaNum].Core == CurrentCoreId &&
(vLinkGen_ZeroInit_Early_GroupsSet.Areas[areaNum].End - vLinkGen_ZeroInit_Early_GroupsSet.Areas[areaNum].Start) > 0)
{
memPtr2 = (volatile uint32 *)vLinkGen_ZeroInit_Early_GroupsSet.Areas[areaNum].Start;
while((uint32)memPtr2 < vLinkGen_ZeroInit_Early_GroupsSet.Areas[areaNum].End)
{
*memPtr2 = 0x0;
memPtr2++;
}
}
}
# endif /*VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS>1uL*/
#else
#error "Mandatory define VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS missing within vLinkGen configuration!"
#endif /*VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS*/
The code is simple , The most important thing is vLinkGen_ZeroInit_Early_GroupsSet This structure , It's defined in :
vLinkGen_Lcfg.c
vLinkGen_Cfg.h
typedef struct
{
/*! Start address of the memory area. */
uint32 Start;
/*! End address of the memory area. */
uint32 End;
/*! Core ID to perform the initialization. */
uint32 Core;
/*! ECC alignment of memory area in bytes. */
uint32 Alignment;
} vLinkGen_MemArea;
typedef struct
{
/*! Number of memory areas in this set. */
uint32 Num;
/*! Table of generic memory areas. */
const vLinkGen_MemArea *Areas;
} vLinkGen_MemAreaSet;
# define VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS 2uL
const vLinkGen_MemArea vLinkGen_ZeroInit_Early_Groups[VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS] =
{
{
/* Brs_Shared_Var */
/* .Start */ (uint32)_Brs_Shared_Var_START,
/* .End */ (uint32)_lc_ge_Brs_Shared_Var,
/* .Core */ 0uL,
/* .Alignment */ 0uL
},
{
0uL,
0uL,
0uL,
0uL
}
};
const vLinkGen_MemAreaSet vLinkGen_ZeroInit_Early_GroupsSet =
{
VLINKGEN_CFG_NUM_ZERO_INIT_EARLY_GROUPS,
vLinkGen_ZeroInit_Early_Groups
};
It can be seen from here that the starting process starts , Define only Core0 Start and end addresses of memory , And send this memory area to 0:
Start address and end address of memory
- /* .Start */ (uint32)_Brs_Shared_Var_START,
- /* .End */ (uint32)_lc_ge_Brs_Shared_Var,
vLinkGen_Template.lsl
group Brs_Shared_Var_PAD (align = 4)
{
reserved "Brs_Shared_Var_PAD" (size = 0);
}
"_Brs_Shared_Var_START" = "_lc_gb_Brs_Shared_Var";
"_Brs_Shared_Var_END" = ("_lc_ge_Brs_Shared_Var" == 0) ? 0 : "_lc_ge_Brs_Shared_Var" - 1;
"_Brs_Shared_Var_LIMIT" = "_lc_ge_Brs_Shared_Var";
"_Brs_Shared_Var_ALL_START" = "_Brs_Shared_Var_START";
"_Brs_Shared_Var_ALL_END" = "_Brs_Shared_Var_END";
"_Brs_Shared_Var_ALL_LIMIT" = "_Brs_Shared_Var_LIMIT";
}
xxx.mapxml
<row>0x7002f920;_lc_gb_Brs_Shared_Var_GROUP;mpe:vtc:linear</row>
<row>0x7002f920;_lc_gb_Brs_Shared_Var;mpe:vtc:linear</row>
...
<row>0x7002f922;_lc_ge_Brs_Shared_Var;mpe:vtc:linear</row>
<row>0x7002f922;_lc_ge_Brs_Shared_Var_GROUP;mpe:vtc:linear</row>
2.2 Set the stack pointer
uint32 CurrentCoreConfig = 0;
uint32 i;
for (i=0; i<BrsMain_CoreConfig_Size; i++)
{
if (BrsMain_CoreConfig[i].PhysicalCoreId == CurrentCoreId)
{
CurrentCoreConfig = i;
i=BrsMain_CoreConfig_Size+1;
}
}
/* Check, if no valid core config was found */
if (i==BrsMain_CoreConfig_Size)
BrsMainExceptionStartup();
BRS_SET_SP(BrsMain_CoreConfig[CurrentCoreConfig].StartupStackEndLabel);
typedef struct
{
/* Logical Id of configured core. */
uint32 LogicalCoreId;
/* Phyiscal Id of configured core. */
uint32 PhysicalCoreId;
/* End label of the startup stack. */
Brs_AddressOfConstType StartupStackEndLabel;
/* startup stack size */
uint32 StartupStackSize;
/* Reflects if core is an AUTOSAR core. */
brsCoreAsrType CoreIsAsr;
/* Start label of the exception vector table. */
Brs_AddressOfConstType ExcVecLabel;
/* Start label of the interrupt vector table. */
Brs_AddressOfConstType IntVecLabel;
}brsMain_CoreType;
const brsMain_CoreType BrsMain_CoreConfig[6u] =
{
{
/* .LogicalCoreId = */ 0u,
/* .PhysicalCoreId = */ 0u,
/* .StartupStackEndLabel = */ (Brs_AddressOfConstType)(&BRSHW_DEFINE_STARTUP_STACK(0)),
/* .StartupStackSize = */ 10240u,
/* .CoreIsAsr = */ ASR,
/* .ExcVecLabel = */ (Brs_AddressOfConstType)(&BRSHW_DEFINE_EXCVEC(0)),
/* .IntVecLabel = */ (Brs_AddressOfConstType)(&BRSHW_DEFINE_INTVEC(0))
},
...
}
stay
Os_Link_Core0.lsl
You can also find the memory address allocation of each core
#if defined ( OS_LINK_EXCVEC_CODE )
if (exists(".text.OS_EXCVEC_CORE0_CODE"))
{
group OS_EXCVEC_CORE0_CODE_GROUP(align=256)
{
select "[.]text.OS_EXCVEC_CORE0_CODE";
}
"_OS_EXCVEC_CORE0_CODE_START" = "_lc_gb_OS_EXCVEC_CORE0_CODE_GROUP";
"_OS_EXCVEC_CORE0_CODE_END" = "_lc_ge_OS_EXCVEC_CORE0_CODE_GROUP" - 1;
"_OS_EXCVEC_CORE0_CODE_LIMIT" = "_lc_ge_OS_EXCVEC_CORE0_CODE_GROUP";
}
else
{
"_OS_EXCVEC_CORE0_CODE_START" = 0;
"_OS_EXCVEC_CORE0_CODE_END" = 0;
"_OS_EXCVEC_CORE0_CODE_LIMIT" = 0;
}
#endif
2.3 Set up PSW
Program Status Word(PSW)
BRS_MOVE_TO_CSFR(BRS_PSW_OFFSET, 0x00000B80UL);
2.4 To configure PCIX
CPUx Previous Context Information Register(PCIX)
AuxVariable = BRS_MOVE_FROM_CSFR(BRS_PCXI_OFFSET);
AuxVariable &= 0xfff00000UL;
BRS_MOVE_TO_CSFR(BRS_PCXI_OFFSET, AuxVariable);
2.5 To configure CSA
The Context Save Areas (CSA)
/* =========================================================================== */
/* initialize the CSAs (inlined function) */
/* aftwerwards function calls are possible */
/* =========================================================================== */
BRS_CSA_Element* CSA;
uint32 NumberOfCSA;
uint32 pcxi_val = 0; /* the last PCXI has to point to 0 */
uint32 CSAReserve = 20; /* the LCX will be initalized to point to 20 CSAs before the very last CSA */
uint32 seg_nr, seg_idx; /* auxiliary variables to assemble the PCXI value */
BRS_CSA_Element* CSAAddress = (BRS_CSA_Element*)((uint32)BrsMain_CoreConfig[CurrentCoreConfig].StartupStackEndLabel - (uint32)BrsMain_CoreConfig[CurrentCoreConfig].StartupStackSize);
uint32 CSA_Numbers = ((uint32)BrsMain_CoreConfig[CurrentCoreConfig].StartupStackEndLabel - 1024 - (uint32)CSAAddress) / 64;
if (CSA_Numbers<512)
{
/* The configured stack size is smaller than the recommended one! * Beginning with vBaseEnv 2.17, the startup stack setup was harmonized and the stack sizes are configured automatically via the vBRS generator * (vBRS/vBRSGeneral/vBRSEnableCoreSpecificVLinkGenConfig). * The recommended default for the Aurix platforms is 33792 Byte, listed in vBaseEnv/vBaseEnvGeneral/vBaseEnvStartupStackSettings/vBaseEnvStackSize. * This means, 1024 Byte for the startup stack and 512 CSA list entries (64 Byte each). There is no separated section for the CSAs anymory, * they are placed in STACK_Cx, together with the startup stack. Be aware, that the CSAs are shared across all execution instances * (vBRS/vBRSGeneral/vBRSExecutionInstance). All instances inherit it from the first execution instance (vBRS/vBRSGeneral/vBRSFirstInstance). * Uncomment this loop, if you intentionally reduced the size of STACK_Cx and ensure it is suitable for your project. * Otherwise, please increase the size of STACK_Cx to at least 33792 (vLinkGenVarSectionGroup/vLinkGenVarSectionGroupSize). */
// while (1);
}
if(CSAAddress != 0)
{
CSA = CSAAddress;
for (NumberOfCSA=0; NumberOfCSA < CSA_Numbers; NumberOfCSA++) /* loop over all CSA elements */
{
*CSA[0] = pcxi_val; /* *CSA is equal to CSA[0] is equal to CSA.PCXI (it holds the PCXI value and has to point to the next PCXI) */
/* now translate the current CSA address to a valid PCXI value which is used within the AURIX registers */
seg_nr = (((unsigned int)CSA >> 28) & 0xf) << 16;
seg_idx = (((unsigned int)CSA >> 6) & 0xffff);
pcxi_val = seg_nr | seg_idx;
if (NumberOfCSA == CSAReserve) /* reserve CSAs for exception handling */
{
BRS_MOVE_TO_CSFR(BRS_LCX_OFFSET, pcxi_val);
}
CSA++; /* point to the next CSA */
}
BRS_MOVE_TO_CSFR(BRS_FCX_OFFSET, pcxi_val);
}
2.6 Interrupt vector table settings
/* Program the brs default vector table base addresses to handle exceptions during startup */
BrsHw_ExceptionTable_Init((Brs_AddressOfConstType)&_Brs_ExcVect_START, BRS_DEFINE_ADDRESS_UNUSED);
3.BrsMainStartup.c
Brs_ApplicationEntry();

The main function of this function is to operate on the memory area ( Clear and assign )
Then enter main(); Function
4.BrsMain.c

stay main() Function, first initialize the interrupt vector table of each core
/* Search for valid Exception- and Interrupt Table in BrsMain_CoreConfig[] (vBRS generated into vBrs_Lcfg.c) */
for (i=0; i<BrsMain_CoreConfig_Size; i++)
{
if (BrsMain_CoreConfig[i].PhysicalCoreId == coreID)
{
BrsHw_ExceptionTable_Init(BrsMain_CoreConfig[i].ExcVecLabel, BrsMain_CoreConfig[i].IntVecLabel);
i=BrsMain_CoreConfig_Size+1;
}
}
/* Check, if no valid core config was found */
if (i==BrsMain_CoreConfig_Size)
BrsMainExceptionHandler(kBrsInvalidCoreConfig, BRSERROR_MODULE_BRSMAIN, (uint16)(__LINE__));
And then initialization RTE
Os_InitMemory();
Os_Init();
then EcuM The module takes over the system
5.EcuM.c

5.1 Set up ECU Boot mode
/* Set the current state of the EcuM to STARTUP */
EcuM_SetModuleState(ECUM_STATE_STARTUP); /* SBSW_ECUM_CSL_VAR_ACCESS */
5.2 To interrupt
/* #30 Set the interrupts on ECUs with programmable interrupts (EcuM_AL_SetProgrammableInterrupts). */
EcuM_AL_SetProgrammableInterrupts();
5.3 Drive list reset
/* #31 Initialize BSW that do not use PostBuild parameters (DriverInitListZero). */
ECUM_DRIVERINITLIST_ZERO();
5.4 Clear the wake-up interrupt source
/* Clear wakeups from all sources */
EcuM_InternalClearWakeupEvent( ECUM_WKSOURCE_ALL_SOURCES);
5.5 Reset timeout wake
/* Reset all wakeup validation timeouts */
for (loopCount = 0u; loopCount < EcuM_GetSizeOfWakeupSourceList(); loopCount++) /* PRQA S 2812 */ /* MD_EcuM_2812 */
{
EcuM_SetValidationTimeoutTable(loopCount, ECUM_NO_VALIDATION_TIMEOUT); /* SBSW_ECUM_ACCESSVALTABLE */
}
5.6 Set up EcuM Pattern
/* Set the current state of the EcuM */
EcuM_SetModuleState(ECUM_STATE_STARTUP_ONE); /* SBSW_ECUM_CSL_VAR_ACCESS */
5.7 start-up OS
/* #51 Start the AUTOSAR OS with the corresponding default appMode. */
EcuM_StartOS( EcuM_GetDefaultAppMode());
6.EcuM_Callout_Stubs.c

EcuM To take over OS After starting According to the nuclear ID Start the cores other than the startup core in turn .
StartCore(coreId, &status);
Last start-up OS
StartOS(appMode);
7.Os_Trap.c
StartCore(coreId, &status);
FUNC(void, OS_CODE) StartCore /* COV_OS_HALPLATFORMTESTEDASMULTICORE */
(
CoreIdType CoreID,
StatusType *Status
)
{
/* #10 If the given status pointer is valid: */
if(OS_UNLIKELY(Os_ErrCheckPointerIsNotNull(Status) != OS_CHECK_FAILED)) /* PRQA S 0315 */ /* MD_Os_Dir1.1_0315 */ /* SBSW_OS_TRAP_ERRCHECKPOINTERISNOTNULL_001 */
{
/* #20 Caller (startup code) is privileged. Call API directly. */
Os_TrapCallStartCore(CoreID, Status); /* SBSW_OS_TRAP_API_USERPOINTER_001 */
}
/* else * Do NOT write the error code to the Status pointer, as it is a NULL_PTR! */
}
OS_FUNC_ATTRIBUTE_DEFINITION(OS_LOCAL_INLINE void, OS_CODE, /* COV_OS_HALPLATFORMTESTEDASMULTICORE */
OS_ALWAYS_INLINE, Os_TrapCallStartCore,
(
CoreIdType CoreID,
StatusType *Status
)) /* COV_OS_HALPLATFORMTESTEDASMULTICORE */
{
/* #10 Inform the trace module on service function entry. */
Os_TraceOrtiApiEntry(OsOrtiApiIdStartCore);
/* #20 Call the API. */
Os_Api_StartCore(CoreID, Status); /* SBSW_OS_FC_PRECONDITION */
/* #30 Inform the trace module on service function exit. */
Os_TraceOrtiApiExit(OsOrtiApiIdStartCore);
}
FUNC(void, OS_CODE) Os_Api_StartCore /* COV_OS_HALPLATFORMMULTICOREUNSUPPORTED */
(
CoreIdType CoreID,
StatusType *Status
)
{
*Status = E_OK; /* PRQA S 2982 */ /* MD_Os_Rule2.2_2982_Config */ /* SBSW_OS_PWA_PRECONDITION */
/* #10 Perform error checks. */
if(Os_CoreIsSingleCoreOs() != 0u) /* COV_OS_TESTSUITERESTRICTIONCORECOUNT */ /* PRQA S 2991, 2992, 2995, 2996 */ /* MD_Os_Rule14.3_2991, MD_Os_Rule14.3_2992, MD_Os_Rule2.2_2995, MD_Os_Rule2.2_2996 */
{
*Status = E_OS_ID; /* SBSW_OS_PWA_PRECONDITION */
}
else if(OS_UNLIKELY(Os_CoreCheckId(CoreID) == OS_CHECK_FAILED ))
{
*Status = E_OS_ID; /* SBSW_OS_PWA_PRECONDITION */
}
else
{
P2CONST(Os_CoreConfigType, AUTOMATIC, OS_CONST) core;
P2CONST(Os_CoreConfigType, AUTOMATIC, OS_CONST) currentCore;
core = Os_CoreId2Core(CoreID);
currentCore = Os_CoreGetCurrentCore();
if(OS_UNLIKELY(Os_CoreCheckIsAsrCore(core) == OS_CHECK_FAILED)) /* SBSW_OS_CORE_CORECHECKISASRCORE_001 */
{
*Status = E_OS_ID; /* SBSW_OS_PWA_PRECONDITION */
}
else
{
if(OS_UNLIKELY(Os_CoreCheckCoreIsInactive(core) == OS_CHECK_FAILED)) /* SBSW_OS_CORE_CORECHECKCOREISINACTIVE_001 */
{
*Status = E_OS_STATE; /* SBSW_OS_PWA_PRECONDITION */
}
else
{
if(Os_CoreIsAsrCore(currentCore) != 0u) /* SBSW_OS_CORE_COREISASRCORE_002 */
{
P2CONST(Os_CoreAsrConfigType, AUTOMATIC, OS_CONST) currentCoreAsr;
currentCoreAsr = Os_Core2AsrCore(currentCore); /* SBSW_OS_CORE_CORE2ASRCORE_002 */
if(OS_UNLIKELY(Os_CoreCheckOsIsNotStarted(currentCoreAsr) == OS_CHECK_FAILED)) /* SBSW_OS_CORE_CORECHECKOSISNOTSTARTED_001 */
{
*Status = E_OS_ACCESS; /* SBSW_OS_PWA_PRECONDITION */
}
}
}
if(OS_LIKELY(*Status == E_OK))
{
P2CONST(Os_CoreAsrConfigType, AUTOMATIC, OS_CONST) coreAsr;
/* #20 Set the core activation request. */
Os_CoreGetStatus(currentCore)->CoreStartRequests[CoreID] = OS_CORESTATE_ACTIVATED_ASR; /* PRQA S 2842 */ /* MD_Os_Rule18.1_2842_Check */ /* SBSW_OS_CORE_COREGETSTATUS_003 */ /* SBSW_OS_CORE_CORESTARTREQUESTS_001 */
coreAsr = Os_Core2AsrCore(core); /* SBSW_OS_CORE_CORE2ASRCORE_001 */
/* #30 Attach to synchronization barrier. */
Os_BarrierAttach(coreAsr->Barrier); /* SBSW_OS_CORE_BARRIERATTACH_001 */
if(core->IsAutostart == TRUE) /* COV_OS_HALPLATFORMNONAUTOSTARTCORE */
{
/* #40 If the core is an auto-start core (already booted), just set the waiting signal. */
OsCfg_CoreBootBarrierRefs[CoreID]->WaitingSign = OS_CORE_BOOTBARRIER_STARTCORE; /* SBSW_OS_CORE_COREBOOTBARRIERREFS_001 */
}
else
{
/* #50 Otherwise start the core through hardware registers. */
Os_Hal_CoreStart(core->HwConfig); /* SBSW_OS_CORE_HAL_CORESTART_001 */
}
}
}
}
}
StartOS(appMode);
FUNC(void, OS_CODE) StartOS
(
AppModeType Mode
)
{
/* #10 Caller (startup code) is privileged. Call API directly. */
Os_TrapCallStartOS(Mode);
}
FUNC(void, OS_CODE) Os_Api_StartOS(AppModeType Mode)
{
/* #10 If the OS is not configured for the interrupt only use case: */
if(Os_CoreIsInterruptOnlyOs() == 0u ) /* PRQA S 2991, 2992, 2995, 2996 */ /* MD_Os_Rule14.3_2991, MD_Os_Rule14.3_2992, MD_Os_Rule2.2_2995, MD_Os_Rule2.2_2996 */
{
P2CONST(Os_CoreConfigType, AUTOMATIC, OS_CONST) core;
core = Os_CoreGetCurrentCore();
/* #10 Check that the core has been started as an AUTOSAR core. */
if(OS_UNLIKELY(Os_CoreCheckStartedAsAsr(core) == OS_CHECK_FAILED)) /* SBSW_OS_CORE_CORECHECKSTARTEDASASR_001 */ /* COV_OS_INVSTATE */
{
Os_ErrKernelPanic();
}
else
{
P2CONST(Os_CoreAsrConfigType, AUTOMATIC, OS_CONST) coreAsr;
coreAsr = Os_Core2AsrCore(core); /* SBSW_OS_CORE_CORE2ASRCORE_002 */
/* #20 Check that StartOS() was not called before. */
if(OS_UNLIKELY(Os_CoreCheckOsIsNotStarted(coreAsr) == OS_CHECK_FAILED)) /* SBSW_OS_CORE_CORECHECKOSISNOTSTARTED_001 */ /* COV_OS_INVSTATE */
{
Os_ErrKernelPanic();
}
else
{
/* #30 Enter the core's Init Hook. */
Os_HookCallOs_CoreInitHook(Os_CoreGetInitHook(coreAsr), Mode); /* SBSW_OS_CORE_COREGETINITHOOK_001 */ /* SBSW_OS_CORE_HOOKCALLOS_COREINITHOOK_001 */
}
}
}
/* #50 Else ignore the call. */
}
边栏推荐
- buuctf-pwn write-ups (9)
- Problems encountered in the project u-parse component rendering problems
- Find in MySQL_ in_ Detailed explanation of set() function usage
- How to write good code defensive programming
- RPC protocol details
- lombok @Builder注解
- sample_rate(采樣率),sample(采樣),duration(時長)是什麼關系
- Oracle日期格式转换 to_date,to_char,to_timetamp 相互转换
- Use JMeter to record scripts and debug
- New words new words new words new words [2]
猜你喜欢

@Extension, @spi annotation principle

Share: ZTE Yuanhang 30 Pro root unlock BL magick ZTE 7532n 8040n 9041n brush mask original brush package root method Download

Find in MySQL_ in_ Detailed explanation of set() function usage

Take a look at semaphore, the current limiting tool provided by JUC

How to obtain the coordinates of the aircraft passing through both ends of the radar

Pytorch yolov5 training custom data

使用JMeter录制脚本并调试

Use JMeter to record scripts and debug

LeetCode 6109. Number of people who know the secret

Rse2020/ cloud detection: accurate cloud detection of high-resolution remote sensing images based on weak supervision and deep learning
随机推荐
Trust counts the number of occurrences of words in the file
RedHat7.4配置yum软件仓库(RHEL7.4)
ICML2022 | 长尾识别中分布外检测的部分和非对称对比学习
技术分享 | 接口测试价值与体系
文章中的逻辑词
Deep copy and shallow copy [interview question 3]
Einstein sum einsum
Icml2022 | partial and asymmetric comparative learning of out of distribution detection in long tail recognition
LeetCode 6111. 螺旋矩阵 IV
Chinese postman? Really powerful!
7-2 保持链表有序
Rse2020/ cloud detection: accurate cloud detection of high-resolution remote sensing images based on weak supervision and deep learning
Is it safe for golden sun to open an account? Can I open an account free of 5 in case?
Various pits of vs2017 QT
RPC协议详解
Solutions contents have differences only in line separators
buuctf-pwn write-ups (9)
SAP feature description
爬虫01-爬虫基本原理讲解
FCN: Fully Convolutional Networks for Semantic Segmentation