การคอมไพล์รหัสเพื่อเรียกใช้จาก RAM ภายนอก


13

ฉันกำลังพิจารณาการออกแบบสำหรับระบบเกมมินิมัลลิสต์ที่ใช้PIC18F85J5 ส่วนหนึ่งของการออกแบบของฉันคือเกมสามารถโหลดได้จากการ์ด SD โดยไม่ต้องทำการชิพโปรแกรมใหม่หรือการกระพริบหน่วยความจำโปรแกรม ฉันเลือกชิพนั้นเพราะมันมีอินเตอร์เฟสหน่วยความจำภายนอกที่จะอนุญาตให้ฉันรันโค้ดจาก SRAM ภายนอก

แนวคิดพื้นฐานคือหน่วยความจำภายในของโปรแกรมจะมีส่วนต่อประสานสำหรับเรียกดูการ์ด SD และเมื่อผู้ใช้เลือกโปรแกรมมันจะคัดลอกไฟล์ฐานสิบหกจากการ์ด sd ไปยังหน่วยความจำภายนอกแล้วกระโดดลงสู่พื้นที่หน่วยความจำภายนอก .

หน่วยความจำโปรแกรมภายในจะมีไลบรารีต่าง ๆ สำหรับกราฟิกอินพุตคอนโทรลเลอร์และยูทิลิตี้ต่าง ๆ

ฉันค่อนข้างมั่นใจว่าฉันรู้วิธีทำให้ชิ้นส่วนเฟิร์มแวร์ภายในทำงานได้ดี ปัญหาคือการสร้างโปรแกรมให้ทำงานจาก RAM ภายนอก มันไม่เหมือนกับการกำหนดเป้าหมายรูปภาพปกติและจำเป็นต้องทราบถึงฟังก์ชั่นห้องสมุดที่มีอยู่ในหน่วยความจำภายใน แต่ไม่ต้องคอมไพล์ใหม่ นอกจากนี้ยังต้องเริ่มใช้ที่อยู่หลังจากแฟลชภายใน 32k ไม่ใช่ที่ศูนย์ มีวิธีที่ดีในการรวบรวมโปรแกรมโดยใช้ข้อ จำกัด ประเภทนี้หรือไม่?

ฉันใช้ MPLab IDE แต่ฉันไม่คุ้นเคยกับมันหรือวิธีการกำหนดเองประเภทนี้


หนึ่งในคำถามที่ดีที่สุดที่ฉันเคยเห็นที่นี่ในขณะที่ ... ฉันรอคอยที่จะได้ยินความคิดและคำตอบ
Jon L

วิธีการทางเลือกที่น่าสนใจบางอย่างถูกกล่าวถึงในelectronics.stackexchange.com/questions/5386/…
Toby Jaffey

ฉันเห็นสิ่งนั้น แต่พวกเขาส่วนใหญ่แนะนำให้เขียนถึงแฟลชซึ่งฉันต้องการหลีกเลี่ยง การออกแบบฮาร์ดแวร์ของฉันจะมีลักษณะคล้ายกับรูปที่ 6 ในบันทึกย่อของแอปพลิเคชันในคำตอบที่ยอมรับได้
captncraig

คำตอบ:


8

คุณมีสองประเด็นแยกจากกัน:

  1. การสร้างรหัสสำหรับช่วงที่อยู่ RAM ภายนอก

    นี่เป็นเรื่องง่ายมาก สิ่งที่คุณต้องทำคือสร้างไฟล์ลิงเกอร์ที่มีเฉพาะช่วงที่อยู่ที่คุณต้องการให้โค้ดครอบครอง โปรดทราบว่าคุณไม่จำเป็นต้องจองช่วงที่อยู่หน่วยความจำของโปรแกรมเฉพาะสำหรับแอพภายนอกเหล่านี้ แต่ยังรวมถึงพื้นที่แรมบางส่วน พื้นที่ RAM นี้เช่นที่อยู่หน่วยความจำของโปรแกรมจะต้องได้รับการแก้ไขและรู้จัก เพียงทำให้ช่วงที่อยู่คงที่และที่รู้จักนั้นพร้อมใช้งานในไฟล์ linker อย่าลืมที่จะทำให้มันไม่สามารถใช้ได้ในไฟล์ลิงเกอร์รหัสฐาน

    หลังจากโหลดโค้ดแอพใหม่ลงในหน่วยความจำภายนอกแล้วจะต้องรู้วิธีการใช้งาน สิ่งที่ง่ายที่สุดน่าจะเป็นการเริ่มต้นการดำเนินการที่ตำแหน่ง RAM ภายนอกแห่งแรก ซึ่งหมายความว่ารหัสของคุณจะต้องมีหนึ่งรหัสส่วนที่อยู่เริ่มต้นที่แน่นอน สิ่งนี้มี GOTO ทางด้านขวาของฉลากในรหัสที่เหลือซึ่งทั้งหมดจะสามารถเปลี่ยนตำแหน่งได้

  2. การเชื่อมโยงแอพเข้ากับรูทีนไลบรารีในรหัสฐาน

    ไม่มีวิธีง่ายๆในการทำเช่นนี้กับเครื่องมือ Microchip ที่มีอยู่ แต่ก็ไม่ได้แย่ขนาดนั้น

    ปัญหาที่ใหญ่กว่าคือวิธีที่คุณต้องการจัดการกับการเปลี่ยนแปลงรหัสฐาน กลยุทธ์ง่าย ๆ คือการสร้างรหัสฐานของคุณเรียกใช้โปรแกรมผ่านไฟล์แผนที่ผลลัพธ์เพื่อรวบรวมที่อยู่ร่วมจากนั้นให้เขียนไฟล์นำเข้าพร้อมกับคำสั่ง EQU สำหรับสัญลักษณ์ที่กำหนดทั่วโลกทั้งหมด ไฟล์นำเข้านี้จะรวมอยู่ในรหัสแอพทั้งหมด ไม่มีสิ่งใดที่จะลิงก์เนื่องจากซอร์สโค้ดของแอปนั้นมีการอ้างอิงที่อยู่ถาวรกับจุดเข้าใช้งานของรหัสฐานเป็นหลัก

    นั่นเป็นเรื่องง่ายที่จะทำและจะทำงานได้ แต่พิจารณาว่าจะเกิดอะไรขึ้นเมื่อคุณเปลี่ยนรหัสฐาน แม้แต่การแก้ไขข้อผิดพลาดเล็กน้อยอาจทำให้ที่อยู่ทั้งหมดเลื่อนไปมาแล้วรหัสแอปที่มีอยู่ทั้งหมดจะไม่ดีและต้องสร้างใหม่ หากคุณไม่เคยวางแผนที่จะให้การอัปเดตรหัสฐานโดยไม่มีการอัปเดตแอปทั้งหมดบางทีคุณอาจหลีกเลี่ยงสิ่งนี้ได้ แต่ฉันคิดว่ามันเป็นความคิดที่ไม่ดี

    วิธีที่ดีกว่าคือการมีพื้นที่ส่วนติดต่อที่กำหนดที่ที่อยู่คงที่ที่เลือกไว้ในรหัสฐาน จะมีหนึ่ง GOTO สำหรับทุกรูทีนย่อยที่รหัสแอปสามารถโทรได้ GOTO เหล่านี้จะถูกวางไว้ที่ที่อยู่คงที่ที่รู้จักและแอพภายนอกจะโทรไปยังที่ตั้งเหล่านั้นเท่านั้นซึ่งจะข้ามไปที่ใดก็ตามที่รูทีนย่อยจบลงด้วยการสร้างรหัสฐานนั้น สิ่งนี้มีค่าใช้จ่าย 2 คำหน่วยความจำของโปรแกรมต่อรูทีนย่อยที่ส่งออกและสองรอบพิเศษ ณ รันไทม์ แต่ฉันคิดว่าคุ้มค่าดี

    ในการทำสิ่งนี้ให้ถูกต้องคุณจะต้องทำให้กระบวนการสร้าง GOTOs โดยอัตโนมัติและไฟล์ส่งออกผลลัพธ์ที่แอพภายนอกจะนำเข้าเพื่อรับรูทีนย่อย (จริง ๆ แล้วคือ GOTO redirector) คุณอาจจะสามารถทำได้ด้วยการใช้ MPASM macros อย่างชาญฉลาด แต่ถ้าฉันทำสิ่งนี้ฉันจะใช้ preprocessor ของฉันอย่างแน่นอนเพราะมันสามารถเขียนไปยังไฟล์ภายนอกได้ในเวลาที่ประมวลผลล่วงหน้า คุณสามารถเขียนแมโครตัวประมวลผลล่วงหน้าเพื่อให้สามารถกำหนดแต่ละตัวเปลี่ยนเส้นทางได้ด้วยรหัสต้นทางบรรทัดเดียว แมโครทำสิ่งที่น่ารังเกียจทั้งหมดภายใต้ประทุนซึ่งเป็นการสร้าง GOTO การอ้างอิงภายนอกไปยังรูทีนเป้าหมายจริงและเพิ่มบรรทัดที่เหมาะสมลงในไฟล์การส่งออกที่มีที่อยู่คงที่ที่รู้จักของรูทีนนั้นด้วยชื่อที่เหมาะสม บางทีมาโครอาจสร้างตัวแปรพรีโปรเซสเซอร์ที่มีชื่อปกติ (เช่นอาเรย์ที่ขยายได้แบบรันไทม์) จากนั้นไฟล์ส่งออกจะถูกเขียนหนึ่งครั้งหลังจากการเรียกมาโครทั้งหมด หนึ่งในหลาย ๆ สิ่งที่พรีโปรเซสเซอร์ของฉันสามารถทำได้นั้นมาโคร MPASM ไม่สามารถทำได้คือการจัดการสตริงเพื่อสร้างชื่อสัญลักษณ์ใหม่จากชื่ออื่น

    preprocessor และพวงของสิ่งอื่น ๆ ที่เกี่ยวข้องของฉันคือสามารถใช้ได้ฟรีที่www.embedinc.com/pic/dload.htm


ฉันชอบความคิดของตารางกระโดดคงที่ ฉันเพิ่งจะเริ่มต้นจากจุดสิ้นสุดของหน่วยความจำแฟลชและมีตำแหน่งคงที่สำหรับการโทรแต่ละระบบ ฉันอาจจะไปด้วยไฟล์ส่วนหัวที่เก็บรักษาด้วยตนเองพร้อมด้วยที่อยู่ของรูทีนย่อยทั้งหมดหากฉันไม่สามารถหาข้อมูลทั้งหมดของตัวประมวลผลล่วงหน้าที่คุณอธิบายได้
captncraig

5

ตัวเลือกที่ 1: ภาษาที่ตีความ

นี่ไม่ได้ตอบคำถามโดยตรง (ซึ่งเป็นคำถามที่ยอดเยี่ยม BTW และฉันหวังว่าจะเรียนรู้จากคำตอบที่ตอบโดยตรง) แต่เป็นเรื่องปกติมากเมื่อทำโครงการที่สามารถโหลดโปรแกรมภายนอกเพื่อเขียนโปรแกรมภายนอกใน ภาษาที่ตีความ หากทรัพยากรมี จำกัด (ซึ่งจะอยู่ในโปรเซสเซอร์นี้คุณคิดว่าจะใช้ PIC32 หรือโปรเซสเซอร์ ARM ขนาดเล็กสำหรับสิ่งนี้หรือไม่) เป็นเรื่องปกติที่จะ จำกัด ภาษาไว้ที่ส่วนย่อยของข้อมูลจำเพาะทั้งหมด ยิ่งไปกว่านั้นเครือข่ายเป็นภาษาเฉพาะโดเมนที่ทำบางสิ่งเท่านั้น

ตัวอย่างเช่นโครงการ eluaเป็นตัวอย่างของภาษาตีความต่ำทรัพยากร (64 kB RAM) คุณสามารถบีบ RAM นี้ลงเหลือ 32k หากคุณลบคุณสมบัติบางอย่าง (หมายเหตุ: มันจะไม่ทำงานกับโปรเซสเซอร์ปัจจุบันของคุณซึ่งเป็นสถาปัตยกรรม 8 บิตการใช้ RAM ภายนอกอาจช้าเกินไปสำหรับกราฟิก) มันให้ภาษาที่รวดเร็วและยืดหยุ่นซึ่งผู้ใช้ใหม่สามารถตั้งโปรแกรมเกมได้อย่างง่ายดายหากคุณมี API น้อยที่สุด มีเอกสารมากมายสำหรับภาษาออนไลน์ มีภาษาอื่น ๆ (เช่น Forth และ Basic) ซึ่งคุณสามารถใช้ในลักษณะที่คล้ายกัน แต่ฉันคิดว่า Lua เป็นตัวเลือกที่ดีที่สุดในขณะนี้

ในหลอดเลือดดำที่คล้ายกันคุณสามารถสร้างภาษาเฉพาะโดเมนของคุณเอง คุณจะต้องจัดทำ API แบบเต็มรูปแบบและเอกสารภายนอกเพิ่มเติม แต่ถ้าเกมคล้ายกันทั้งหมดนี้จะไม่ยากเกินไป

ไม่ว่าในกรณีใดก็ตาม PIC18 อาจไม่ใช่โปรเซสเซอร์ที่ฉันใช้สำหรับสิ่งที่เกี่ยวข้องกับการเขียนโปรแกรม / สคริปต์และกราฟิกแบบกำหนดเอง คุณอาจคุ้นเคยกับโปรเซสเซอร์ระดับนี้ แต่ฉันขอแนะนำว่านี่จะเป็นเวลาที่ดีในการใช้บางอย่างกับไดรเวอร์การแสดงผลและหน่วยความจำเพิ่มเติม

ตัวเลือกที่ 2: เพียงแค่ reprogram สิ่งทั้งหมด

อย่างไรก็ตามหากคุณวางแผนที่จะเขียนโปรแกรมเกมทั้งหมดด้วยตัวเองใน C แล้วอย่ากังวลกับการโหลดเฉพาะลอจิกเกมจากการ์ด SD คุณมี Flash เพียง 32kB ในการ reprogram และสามารถรับการ์ด microSD ขนาด 4 GB ได้อย่างง่ายดาย (หมายเหตุ: การ์ดขนาดใหญ่มักจะเป็น SDHC ซึ่งยากต่อการเชื่อมต่อด้วย) สมมติว่าคุณใช้ทุกไบต์สุดท้ายของ 32 kB ที่เหลืออยู่ในการ์ด SD สำหรับเฟิร์มแวร์ 131,072 สำเนาของคุณด้วยตรรกะของเกมที่คุณต้องการ

มีมากมายของ appnotes มีการเขียน bootloaders สำหรับภาพเช่นAN851 คุณจะต้องออกแบบ bootloader ของคุณเพื่อครอบครองพื้นที่ของหน่วยความจำที่เฉพาะเจาะจง (อาจด้านบนของพื้นที่หน่วยความจำคุณจะระบุสิ่งนี้ใน linker) และระบุว่าโครงการเฟิร์มแวร์เต็มไม่ถึงภูมิภาคนี้ Appnote สะกดรายละเอียดนี้ให้ชัดเจนยิ่งขึ้น เพียงแค่แทนที่ "ส่วนการบูตของ PIC18F452" ด้วย "ส่วนการบูตที่ฉันระบุในตัวเชื่อมโยง" และมันจะสมเหตุสมผล

จากนั้น bootloader ของคุณเพียงต้องการอนุญาตให้ผู้ใช้เลือกโปรแกรมที่จะเรียกใช้จากการ์ด SD และคัดลอกสิ่งทั้งหมด UI อาจเป็นไปได้ว่าผู้ใช้ต้องกดปุ่มค้างไว้เพื่อเข้าสู่โหมดการเลือก ตามปกติแล้ว bootloader จะตรวจสอบสถานะของปุ่มนี้เมื่อทำการรีเซ็ตและหากยังไม่ถูกระงับให้ทำการบูทเข้าสู่เกม หากถูกระงับมันจะต้องอนุญาตให้ผู้ใช้เลือกไฟล์บนการ์ด SD คัดลอกโปรแกรมไปและทำการบู๊ตในเกม [ใหม่] ต่อไป

นี่คือคำแนะนำปัจจุบันของฉัน

ตัวเลือก 3: เวทย์มนตร์ลึกที่เกี่ยวข้องกับการจัดเก็บเฉพาะส่วนหนึ่งของไฟล์ฐานสิบหก

ปัญหาเกี่ยวกับกลไกการมองเห็นของคุณคือโปรเซสเซอร์ไม่ได้จัดการกับ API และการเรียกใช้ฟังก์ชั่นมันเกี่ยวข้องกับตัวเลข - ที่อยู่ที่ตัวชี้คำสั่งสามารถกระโดดได้และคาดว่าจะมีรหัสซึ่งประมวลผลการเรียกใช้ฟังก์ชัน ถ้าคุณพยายามที่จะรวบรวมเป็นเพียงแค่ส่วนหนึ่งของโปรแกรมที่เป็นลิงเกอร์จะไม่ทราบว่าจะทำอย่างไรเมื่อคุณโทรหรือcheck_button_status() toggle_led()คุณอาจรู้ว่ามีฟังก์ชั่นเหล่านั้นอยู่ในไฟล์ hex บนโปรเซสเซอร์ แต่จำเป็นต้องรู้อย่างแม่นยำว่ามีที่อยู่ใด

ตัวเชื่อมโยงแบ่งรหัสของคุณออกเป็นหลายส่วนแล้ว ในทางทฤษฎีคุณสามารถแยกส่วนนี้ออกเป็นส่วนเพิ่มเติมด้วยบางส่วน-sectionและ#pragmaคาถา ฉันไม่เคยทำสิ่งนี้และไม่รู้วิธีการ จนกว่าทั้งสองวิธีข้างต้นจะทำให้ฉันล้มเหลว (หรือมีคนโพสต์คำตอบที่ดีที่สุดที่นี่) ฉันอาจจะไม่เรียนรู้กลไกนี้และดังนั้นฉันจึงไม่สามารถสอนคุณได้


ข้อขัดข้องของฉันต่อหมายเลข 2 คือหน่วยความจำแฟลชมีจำนวนรอบการลบที่ จำกัด ในช่วงอายุของชิป ฉันไม่ต้องการที่จะใช้ mcu beefier เพราะฉันจะสำหรับ minimalism 8 บิต
captncraig

@CMP - มีการลบอย่างน้อย 10,000 รอบ หากมีคนเล่นเกมที่แตกต่างกันทุกวันมันจะคงอยู่จนถึงปี 2582 แฟลชจะอยู่ได้นานกว่าวงจรที่เหลือ ฉันไม่คิดว่าคุณจะต้องกังวลเกี่ยวกับเรื่องนี้เว้นแต่ว่ามันจะอยู่ในอาร์เคดที่จะเล่นหลายสิบครั้งในแต่ละวัน
Kevin Vermeer

1
ประการที่สองความเรียบง่ายแบบ 8 บิตอาจดูเท่ห์ แต่ก็น่าสนใจสำหรับการเขียนโปรแกรม มันง่ายกว่ามากที่จะได้ไมโครคอนโทรลเลอร์ที่แข็งแรงและทำให้ดูย้อนยุคกว่าที่จะถูกบังคับให้ทำให้ดูย้อนยุคเพราะคุณกำลังผลักดันขีด จำกัด ของโปรเซสเซอร์
Kevin Vermeer

1
ทั้งสองคะแนนที่ยุติธรรมมาก แม้ว่าจะมีจำนวนน้อย แต่ pic32 ก็ไม่ได้แตกต่างกันในด้านราคาหรือส่วนประกอบภายนอกมากกว่านี้และถ้ามีแฟลชภายใน 512K มันก็จะชนะ
captncraig

ดูเหมือนว่าวิธีแก้ปัญหาในทางปฏิบัติจะใช้ pic32 และเขียน bootloader เพื่อ reflash จาก sd card ฉันจะมีฟังก์ชั่นการใช้งานซ้ำทั้ง bootloader และรหัสผู้ใช้จะต้องใช้ยาก แต่ตราบใดที่ฉันเก็บไว้ในแฟลชบูท 12k ก็ควรให้ชิพรหัสผู้ใช้ทั้งหมดและพวกเขาสามารถรวมไลบรารีใด ๆ ที่พวกเขาต้องการที่แหล่งที่มา ชั้น
captncraig
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.