diff --git a/ci-scripts/cls_physim.py b/ci-scripts/cls_physim.py
new file mode 100644
index 0000000000000000000000000000000000000000..c06c818327e71a2145e63e6a484ddc66ede344d3
--- /dev/null
+++ b/ci-scripts/cls_physim.py
@@ -0,0 +1,155 @@
+# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+# * contributor license agreements.  See the NOTICE file distributed with
+# * this work for additional information regarding copyright ownership.
+# * The OpenAirInterface Software Alliance licenses this file to You under
+# * the OAI Public License, Version 1.1  (the "License"); you may not use this file
+# * except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# *      http://www.openairinterface.org/?page_id=698
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# *-------------------------------------------------------------------------------
+# * For more information about the OpenAirInterface (OAI) Software Alliance:
+# *      contact@openairinterface.org
+# */
+#---------------------------------------------------------------------
+# Python for CI of OAI-eNB + COTS-UE
+#
+#   Required Python Version
+#     Python 3.x
+#
+#   Required Python Package
+#     pexpect
+#---------------------------------------------------------------------
+
+#to create a SSH object locally in the methods
+import sshconnection
+#to update the HTML object
+import html
+from multiprocessing import SimpleQueue
+
+
+class PhySim:
+	def __init__(self):
+		self.buildargs = ""
+		self.runargs = ""
+		self.eNBIpAddr = ""
+		self.eNBUserName = ""
+		self.eNBPassWord = ""
+		self.eNBSourceCodePath = ""
+		self.ranRepository = ""
+		self.ranBranch = ""
+		self.ranCommitID= ""
+		self.ranAllowMerge= ""
+		self.ranTargetBranch= ""
+		self.exitStatus=0
+		self.workSpacePath=''
+		self.buildLogFile='compile_oai_enb.log'
+		self.runLogFile='ldpctest_run_results.log'
+		self.runResults=[]
+
+	def __CheckResults_PhySim(self,HTML,CONST):
+		mySSH = sshconnection.SSHConnection()
+		mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
+		#retrieve run log file locally$
+		mySSH.copyin(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord, self.workSpacePath+self.runLogFile, '.')
+		mySSH.close()
+		with open(self.runLogFile) as f:
+			for line in f:
+				if 'mean' in line:
+					self.runResults.append(line)
+		info=self.runResults[-1]+self.runResults[-2]
+
+		html_cell = '<pre style="background-color:white">' + info  + '</pre>'
+		html_queue=SimpleQueue()
+		html_queue.put(html_cell)
+		HTML.CreateHtmlTestRowQueue(self.runargs, 'OK', 1, html_queue)
+		return HTML
+
+
+	def __CheckBuild_PhySim(self, HTML, CONST):
+		self.workSpacePath=self.eNBSourceCodePath+'/cmake_targets/'
+		mySSH = sshconnection.SSHConnection()
+		mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
+		#retrieve compile log file locally
+		mySSH.copyin(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord, self.workSpacePath+self.buildLogFile, '.')
+		#delete older run log file
+		mySSH.command('rm ' + self.workSpacePath+self.runLogFile, '\$', 5)
+		mySSH.close()
+		#check build result from local compile log file
+		buildStatus=False
+		with open(self.buildLogFile) as f:
+			if 'nr_prachsim compiled' in f.read():
+				buildStatus=True
+		#update HTML based on build status
+		if buildStatus:
+			HTML.CreateHtmlTestRow(self.buildargs, 'OK', CONST.ALL_PROCESSES_OK, 'LDPC')
+			self.exitStatus=0
+		else:
+			logging.error('\u001B[1m Building OAI UE Failed\u001B[0m')
+			HTML.CreateHtmlTestRow(self.buildargs, 'KO', CONST.ALL_PROCESSES_OK, 'LDPC')
+			HTML.CreateHtmlTabFooter(False)
+			self.exitStatus=1
+		return HTML
+
+	def Build_PhySim(self,htmlObj,constObj):
+		mySSH = sshconnection.SSHConnection()    
+		mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
+
+		#create working dir    
+		mySSH.command('mkdir -p ' + self.eNBSourceCodePath, '\$', 5)
+		mySSH.command('cd ' + self.eNBSourceCodePath, '\$', 5)
+
+		if not self.ranRepository.lower().endswith('.git'):
+			self.ranRepository+='.git'
+
+		#git clone
+		mySSH.command('if [ ! -e .git ]; then stdbuf -o0 git clone '  + self.ranRepository + ' .; else stdbuf -o0 git fetch --prune; fi', '\$', 600)
+		#git config 
+		mySSH.command('git config user.email "jenkins@openairinterface.org"', '\$', 5)
+		mySSH.command('git config user.name "OAI Jenkins"', '\$', 5) 
+
+		#git clean
+		mySSH.command('echo ' + self.eNBPassWord + ' | sudo -S git clean -x -d -ff', '\$', 30)
+
+		# if the commit ID is provided, use it to point to it
+		if self.ranCommitID != '':
+			mySSH.command('git checkout -f ' + self.ranCommitID, '\$', 5)
+		# if the branch is not develop, then it is a merge request and we need to do 
+		# the potential merge. Note that merge conflicts should have already been checked earlier
+		if (self.ranAllowMerge):
+			if self.ranTargetBranch == '':
+				if (self.ranBranch != 'develop') and (self.ranBranch != 'origin/develop'):
+					mySSH.command('git merge --ff origin/develop -m "Temporary merge for CI"', '\$', 5)
+			else:
+				print('Merging with the target branch: ' + self.ranTargetBranch)
+				mySSH.command('git merge --ff origin/' + self.ranTargetBranch + ' -m "Temporary merge for CI"', '\$', 5)
+
+		#build
+		mySSH.command('source oaienv', '\$', 5)
+		mySSH.command('cd cmake_targets', '\$', 5)
+		mySSH.command('mkdir -p log', '\$', 5)
+		mySSH.command('chmod 777 log', '\$', 5)
+		mySSH.command('stdbuf -o0 ./build_oai ' + self.buildargs + ' 2>&1 | stdbuf -o0 tee compile_oai_enb.log', 'Bypassing the Tests|build have failed', 1500) 
+
+		mySSH.close()
+		#check build status and update HTML object
+		lHTML = html.HTMLManagement()
+		lHTML=self.__CheckBuild_PhySim(htmlObj,constObj)
+		return lHTML
+
+
+	def Run_PhySim(self,htmlObj,constObj):
+		mySSH = sshconnection.SSHConnection()
+		mySSH.open(self.eNBIpAddr, self.eNBUserName, self.eNBPassWord)
+		mySSH.command('cd '+self.workSpacePath,'\$',5)
+		mySSH.command(self.workSpacePath+'phy_simulators/build/ldpctest ' + self.runargs + ' >> '+self.runLogFile, '\$', 30)   
+		mySSH.close()
+		lHTML = html.HTMLManagement()
+		lHTML=self.__CheckResults_PhySim(htmlObj,constObj)
+		return lHTML
diff --git a/ci-scripts/main.py b/ci-scripts/main.py
index e9313051fc1bec888dfdcbec50c8998e01d02f93..164b4fc6afd97f0e9dff1497ce05ff4858afaf46 100644
--- a/ci-scripts/main.py
+++ b/ci-scripts/main.py
@@ -30,6 +30,7 @@
 
 import constants as CONST
 
+
 #-----------------------------------------------------------
 # Import
 #-----------------------------------------------------------
@@ -3061,7 +3062,7 @@ class OaiCiTest():
 		logging.debug('\u001B[1m----------------------------------------\u001B[0m')
 
 def CheckClassValidity(action,id):
-	if action != 'Build_eNB' and action != 'WaitEndBuild_eNB' and action != 'Initialize_eNB' and action != 'Terminate_eNB' and action != 'Initialize_UE' and action != 'Terminate_UE' and action != 'Attach_UE' and action != 'Detach_UE' and action != 'Build_OAI_UE' and action != 'Initialize_OAI_UE' and action != 'Terminate_OAI_UE' and action != 'DataDisable_UE' and action != 'DataEnable_UE' and action != 'CheckStatusUE' and action != 'Ping' and action != 'Iperf' and action != 'Reboot_UE' and action != 'Initialize_FlexranCtrl' and action != 'Terminate_FlexranCtrl' and action != 'Initialize_HSS' and action != 'Terminate_HSS' and action != 'Initialize_MME' and action != 'Terminate_MME' and action != 'Initialize_SPGW' and action != 'Terminate_SPGW' and action != 'Initialize_CatM_module' and action != 'Terminate_CatM_module' and action != 'Attach_CatM_module' and action != 'Detach_CatM_module' and action != 'Ping_CatM_module' and action != 'IdleSleep' and action != 'Perform_X2_Handover':
+	if action!='Build_PhySim' and action!='Run_PhySim' and  action != 'Build_eNB' and action != 'WaitEndBuild_eNB' and action != 'Initialize_eNB' and action != 'Terminate_eNB' and action != 'Initialize_UE' and action != 'Terminate_UE' and action != 'Attach_UE' and action != 'Detach_UE' and action != 'Build_OAI_UE' and action != 'Initialize_OAI_UE' and action != 'Terminate_OAI_UE' and action != 'DataDisable_UE' and action != 'DataEnable_UE' and action != 'CheckStatusUE' and action != 'Ping' and action != 'Iperf' and action != 'Reboot_UE' and action != 'Initialize_FlexranCtrl' and action != 'Terminate_FlexranCtrl' and action != 'Initialize_HSS' and action != 'Terminate_HSS' and action != 'Initialize_MME' and action != 'Terminate_MME' and action != 'Initialize_SPGW' and action != 'Terminate_SPGW' and action != 'Initialize_CatM_module' and action != 'Terminate_CatM_module' and action != 'Attach_CatM_module' and action != 'Detach_CatM_module' and action != 'Ping_CatM_module' and action != 'IdleSleep' and action != 'Perform_X2_Handover':
 		logging.debug('ERROR: test-case ' + id + ' has wrong class ' + action)
 		return False
 	return True
@@ -3208,6 +3209,11 @@ def GetParametersFromXML(action):
 			else:
 				CiTestObj.x2_ho_options = string_field
 
+	if action == 'Build_PhySim':
+		ldpc.buildargs  = test.findtext('physim_build_args')
+
+	if action == 'Run_PhySim':
+		ldpc.runargs = test.findtext('physim_run_args')
 
 #check if given test is in list
 #it is in list if one of the strings in 'list' is at the beginning of 'test'
@@ -3243,6 +3249,9 @@ EPC.SetHtmlObj(HTML)
 RAN.SetHtmlObj(HTML)
 RAN.SetEpcObj(EPC)
 
+import cls_physim           #class PhySim for physical simulators build and test
+ldpc=cls_physim.PhySim()    #create an instance for LDPC test using GPU or CPU build
+
 argvs = sys.argv
 argc = len(argvs)
 cwd = os.getcwd()
@@ -3264,12 +3273,14 @@ while len(argvs) > 1:
 		CiTestObj.ranRepository = matchReg.group(1)
 		RAN.SetranRepository(matchReg.group(1))
 		HTML.SetranRepository(matchReg.group(1))
+		ldpc.ranRepository=matchReg.group(1)
 	elif re.match('^\-\-eNB_AllowMerge=(.+)$|^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE):
 		if re.match('^\-\-eNB_AllowMerge=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNB_AllowMerge=(.+)$', myArgv, re.IGNORECASE)
 		else:
 			matchReg = re.match('^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE)
 		doMerge = matchReg.group(1)
+		ldpc.ranAllowMerge=matchReg.group(1)
 		if ((doMerge == 'true') or (doMerge == 'True')):
 			CiTestObj.ranAllowMerge = True
 			RAN.SetranAllowMerge(True)
@@ -3282,6 +3293,7 @@ while len(argvs) > 1:
 		CiTestObj.ranBranch = matchReg.group(1)
 		RAN.SetranBranch(matchReg.group(1))
 		HTML.SetranBranch(matchReg.group(1))
+		ldpc.ranBranch=matchReg.group(1)
 	elif re.match('^\-\-eNBCommitID=(.*)$|^\-\-ranCommitID=(.*)$', myArgv, re.IGNORECASE):
 		if re.match('^\-\-eNBCommitID=(.*)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNBCommitID=(.*)$', myArgv, re.IGNORECASE)
@@ -3290,6 +3302,7 @@ while len(argvs) > 1:
 		CiTestObj.ranCommitID = matchReg.group(1)
 		RAN.SetranCommitID(matchReg.group(1))
 		HTML.SetranCommitID(matchReg.group(1))
+		ldpc.ranCommitID=matchReg.group(1)
 	elif re.match('^\-\-eNBTargetBranch=(.*)$|^\-\-ranTargetBranch=(.*)$', myArgv, re.IGNORECASE):
 		if re.match('^\-\-eNBTargetBranch=(.*)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNBTargetBranch=(.*)$', myArgv, re.IGNORECASE)
@@ -3298,10 +3311,12 @@ while len(argvs) > 1:
 		CiTestObj.ranTargetBranch = matchReg.group(1)
 		RAN.SetranTargetBranch(matchReg.group(1))
 		HTML.SetranTargetBranch(matchReg.group(1))
+		ldpc.ranTargetBranch=matchReg.group(1)
 	elif re.match('^\-\-eNBIPAddress=(.+)$|^\-\-eNB[1-2]IPAddress=(.+)$', myArgv, re.IGNORECASE):
 		if re.match('^\-\-eNBIPAddress=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNBIPAddress=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNBIPAddress(matchReg.group(1))
+			ldpc.eNBIpAddr=matchReg.group(1)
 		elif re.match('^\-\-eNB1IPAddress=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNB1IPAddress=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNB1IPAddress(matchReg.group(1))
@@ -3312,6 +3327,7 @@ while len(argvs) > 1:
 		if re.match('^\-\-eNBUserName=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNBUserName=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNBUserName(matchReg.group(1))
+			ldpc.eNBUserName=matchReg.group(1)
 		elif re.match('^\-\-eNB1UserName=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNB1UserName=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNB1UserName(matchReg.group(1))
@@ -3322,6 +3338,7 @@ while len(argvs) > 1:
 		if re.match('^\-\-eNBPassword=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNBPassword=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNBPassword(matchReg.group(1))
+			ldpc.eNBPassWord=matchReg.group(1)
 		elif re.match('^\-\-eNB1Password=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNB1Password=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNB1Password(matchReg.group(1))
@@ -3332,6 +3349,7 @@ while len(argvs) > 1:
 		if re.match('^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNBSourceCodePath(matchReg.group(1))
+			ldpc.eNBSourceCodePath=matchReg.group(1)
 		elif re.match('^\-\-eNB1SourceCodePath=(.+)$', myArgv, re.IGNORECASE):
 			matchReg = re.match('^\-\-eNB1SourceCodePath=(.+)$', myArgv, re.IGNORECASE)
 			RAN.SeteNB1SourceCodePath(matchReg.group(1))
@@ -3702,6 +3720,11 @@ elif re.match('^TesteNB$', mode, re.IGNORECASE) or re.match('^TestUE$', mode, re
 					CiTestObj.IdleSleep()
 				elif action == 'Perform_X2_Handover':
 					CiTestObj.Perform_X2_Handover()
+				elif action == 'Build_PhySim':
+					HTML=ldpc.Build_PhySim(HTML,CONST)
+					if ldpc.exitStatus==1:sys.exit()
+				elif action == 'Run_PhySim':
+					ldpc.Run_PhySim(HTML,CONST)
 				else:
 					sys.exit('Invalid action')
 		CiTestObj.FailReportCnt += 1
diff --git a/ci-scripts/xml_files/rh_ldpc_gpu_test.xml b/ci-scripts/xml_files/rh_ldpc_gpu_test.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6df90915d3b8432577f84d0464868c654142c552
--- /dev/null
+++ b/ci-scripts/xml_files/rh_ldpc_gpu_test.xml
@@ -0,0 +1,50 @@
+<!--
+
+ Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The OpenAirInterface Software Alliance licenses this file to You under
+ the OAI Public License, Version 1.1  (the "License"); you may not use this file
+ except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.openairinterface.org/?page_id=698
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ For more information about the OpenAirInterface (OAI) Software Alliance:
+      contact@openairinterface.org
+
+ 030105 040301 040502 040606 040601 040603 040608 040605 040646 040641 040643 040648 040645 040401 040201 030201
+-->
+<testCaseList>
+	<htmlTabRef>test-ldpc-gpu</htmlTabRef>
+	<htmlTabName>Test-ldpc-GPU</htmlTabName>
+	<htmlTabIcon>tasks</htmlTabIcon>
+	<repeatCount>1</repeatCount>
+	<TestCaseRequestedList>000001 000002 000003</TestCaseRequestedList>
+	<TestCaseExclusionList></TestCaseExclusionList>
+
+	<testCase id="000001">
+		<class>Build_PhySim</class>
+		<desc>Build for physical simulator</desc>
+		<physim_build_args>--phy_simulators</physim_build_args>
+	</testCase>
+
+	<testCase id="000002">
+		<class>Run_PhySim</class>
+		<desc>Run LDPC Test with CPU</desc>
+		<physim_run_args>-l 8448 -s10 -n100</physim_run_args>
+	</testCase>
+
+	<testCase id="000003">
+		<class>Run_PhySim</class>
+		<desc>Run LDPC Test with GPU</desc>
+		<physim_run_args>-l 8448 -s10 -n100 -G 1</physim_run_args>
+	</testCase>
+
+</testCaseList>