في الجزء السابق ، قمنا بتجميع متحكم دون أي ذاكرة الوصول العشوائي على أساس Altera / Intel FPGA. ومع ذلك ، تحتوي اللوحة على موصل مزود بتقنية SO-DIMM DDR2 بسرعة 1 جيجابت ، والتي من الواضح أنني أريد استخدامها. للقيام بذلك ، نحتاج إلى التفاف وحدة التحكم DDR2 بواجهة ALTMEMPHY
في وحدة نمطية يمكن فهمها لبروتوكول ذاكرة TileLink المستخدم خلال RocketChip. تحت خفض التصحيح عن طريق اللمس ، والبرمجة القوة الغاشمة و RAKE.
كما تعلم ، لدى Computer Science مشكلتين رئيسيتين: إبطال ذاكرة التخزين المؤقت والتسمية المتغيرة. في KDPV ، تشاهد لحظة نادرة - مشكلتان رئيسيتان واجهتهما CS و يخططون لشيء ما .
إخلاء المسئولية: بالإضافة إلى التحذير من المقالة السابقة ، أوصي بشدة أن تقرأ المقالة حتى النهاية قبل تكرار التجارب ، وذلك لتجنب إتلاف FPGA أو وحدة الذاكرة أو دوائر الطاقة.
هذه المرة أردت ، إن لم يكن التمهيد لنظام التشغيل Linux ، قم على الأقل بتوصيل ذاكرة الوصول العشوائي ، والتي تحتوي على لوحتي بالفعل غيغابايت بالكامل (أو يمكنك وضع ما يصل إلى أربعة). يُقترح معيار النجاح للنظر في القدرة على القراءة والكتابة من خلال مجموعة من GDB + OpenOCD ، بما في ذلك العناوين التي لا تتم محاذاتها بمقدار 16 بايت (عرض طلب واحد على الذاكرة). للوهلة الأولى ، تحتاج فقط إلى إصلاح التكوين قليلاً ، لا يمكن لمولّد SoC دعم ذاكرة الوصول العشوائي (RAM) خارج الصندوق. يدعمها ، ولكن من خلال واجهة MIG (جيدًا ، وربما بعض الواجهات الأخرى من Microsemi). من خلال الواجهة القياسية ، يدعم AXI4 أيضًا ، لكن ، كما أفهمها ، ليس من السهل الحصول عليها (على الأقل ، وليس إتقان Platform Platform).
الانحدار الغنائي: بقدر ما أفهم ، هناك سلسلة شائعة من واجهات AXI "داخل الشريحة" التي طورها ARM. هنا يمكن للمرء أن يعتقد أن كل شيء على براءة اختراع ومغلقة. لكن بعد أن قمت بالتسجيل (بدون أي "برامج جامعية" وأي شيء آخر - فقط عن طريق البريد الإلكتروني وملء الاستبيان) وحصلت على المواصفات ، فوجئت بسرور. بالطبع ، أنا لست محامياً ، لكن يبدو أن المعيار مفتوح إلى حد ما: عليك إما استخدام نواة مرخصة من ARM ، أو لا تدعي على الإطلاق أن تكون متوافقًا مع ARM ، وبعد ذلك يبدو أن كل شيء على ما يرام . لكن بشكل عام ، بالطبع ، اقرأ الترخيص ، اقرأ مع المحامين ، إلخ.
القرد و TileLink (الخرافة)
تبدو المهمة بسيطة للغاية ، ولقد فتحت وصف لوحة وحدة ddr2_64bit
المتوفرة بالفعل في المشروع من المورد:
الملكية إنتل وعموما module ddr2_64bit ( local_address, local_write_req, local_read_req, local_burstbegin, local_wdata, local_be, local_size, global_reset_n, pll_ref_clk, soft_reset_n, local_ready, local_rdata, local_rdata_valid, local_refresh_ack, local_init_done, reset_phy_clk_n, mem_odt, mem_cs_n, mem_cke, mem_addr, mem_ba, mem_ras_n, mem_cas_n, mem_we_n, mem_dm, phy_clk, aux_full_rate_clk, aux_half_rate_clk, reset_request_n, mem_clk, mem_clk_n, mem_dq, mem_dqs); input [25:0] local_address; input local_write_req; input local_read_req; input local_burstbegin; input [127:0] local_wdata; input [15:0] local_be; input [2:0] local_size; input global_reset_n; input pll_ref_clk; input soft_reset_n; output local_ready; output [127:0] local_rdata; output local_rdata_valid; output local_refresh_ack; output local_init_done; output reset_phy_clk_n; output [1:0] mem_odt; output [1:0] mem_cs_n; output [1:0] mem_cke; output [13:0] mem_addr; output [1:0] mem_ba; output mem_ras_n; output mem_cas_n; output mem_we_n; output [7:0] mem_dm; output phy_clk; output aux_full_rate_clk; output aux_half_rate_clk; output reset_request_n; inout [1:0] mem_clk; inout [1:0] mem_clk_n; inout [63:0] mem_dq; inout [7:0] mem_dqs; ...
تقول الحكمة الشعبية: "يجب أن تبدأ أي وثائق باللغة الروسية بالكلمات:" لذا ، فهي لا تعمل "." لكن الواجهة هنا ليست بديهية تمامًا ، لذلك لا نزال نقرأها . في الوصف ، قيل لنا على الفور أن العمل مع DDR2 ليس بالأمر السهل. تحتاج إلى تكوين PLL ، وتنفيذ بعض المعايرة ، crack-fex-pex ، يتم local_init_done
إشارة local_init_done
، يمكنك العمل. بشكل عام ، يكون منطق التسمية هنا كما يلي تقريبًا: الأسماء ذات البادئات local_
هي واجهة "المستخدم" ، mem_
يتم إخراج منافذ mem_
مباشرة إلى الأرجل المتصلة بوحدة الذاكرة ، ويجب إرسال pll_ref_clk
إلى إشارة ساعة مع التردد المحدد عند إعداد الوحدة - سيتم استلام الباقي منها ترددات ، حسنا ، كل أنواع المدخلات والمخرجات إعادة تعيين ومخرجات التردد ، والتي يجب أن تعمل مع واجهة المستخدم بشكل متزامن.
لنقم بإنشاء وصف للإشارات الخارجية على الذاكرة وواجهة وحدة ddr2_64bit
:
سمة memif trait MemIf { val local_init_done = Output(Bool()) val global_reset_n = Input(Bool()) val pll_ref_clk = Input(Clock()) val soft_reset_n = Input(Bool()) val reset_phy_clk_n = Output(Clock()) val mem_odt = Output(UInt(2.W)) val mem_cs_n = Output(UInt(2.W)) val mem_cke = Output(UInt(2.W)) val mem_addr = Output(UInt(14.W)) val mem_ba = Output(UInt(2.W)) val mem_ras_n = Output(UInt(1.W)) val mem_cas_n = Output(UInt(1.W)) val mem_we_n = Output(UInt(1.W)) val mem_dm = Output(UInt(8.W)) val phy_clk = Output(Clock()) val aux_full_rate_clk = Output(Clock()) val aux_half_rate_clk = Output(Clock()) val reset_request_n = Output(Bool()) val mem_clk = Analog(2.W) val mem_clk_n = Analog(2.W) val mem_dq = Analog(64.W) val mem_dqs = Analog(8.W) def connectFrom(mem_if: MemIf): Unit = { local_init_done := mem_if.local_init_done mem_if.global_reset_n := global_reset_n mem_if.pll_ref_clk := pll_ref_clk mem_if.soft_reset_n := soft_reset_n reset_phy_clk_n := mem_if.reset_phy_clk_n mem_odt <> mem_if.mem_odt mem_cs_n <> mem_if.mem_cs_n mem_cke <> mem_if.mem_cke mem_addr <> mem_if.mem_addr mem_ba <> mem_if.mem_ba mem_ras_n <> mem_if.mem_ras_n mem_cas_n <> mem_if.mem_cas_n mem_we_n <> mem_if.mem_we_n mem_dm <> mem_if.mem_dm mem_clk <> mem_if.mem_clk mem_clk_n <> mem_if.mem_clk_n mem_dq <> mem_if.mem_dq mem_dqs <> mem_if.mem_dqs phy_clk := mem_if.phy_clk aux_full_rate_clk := mem_if.aux_full_rate_clk aux_half_rate_clk := mem_if.aux_half_rate_clk reset_request_n := mem_if.reset_request_n } } class MemIfBundle extends Bundle with MemIf
فئة dd2_64bit class ddr2_64bit extends BlackBox { override val io = IO(new MemIfBundle { val local_address = Input(UInt(26.W)) val local_write_req = Input(Bool()) val local_read_req = Input(Bool()) val local_burstbegin = Input(Bool()) val local_wdata = Input(UInt(128.W)) val local_be = Input(UInt(16.W)) val local_size = Input(UInt(3.W)) val local_ready = Output(Bool()) val local_rdata = Output(UInt(128.W)) val local_rdata_valid = Output(Bool()) val local_refresh_ack = Output(Bool()) }) }
هنا كانت المجموعة الأولى من المكابس في انتظاري: أولاً ، ROMGenerator
إلى فئة ROMGenerator
، اعتقدت أنه يمكن سحب وحدة التحكم في الذاكرة من أعماق التصميم من خلال متغير عالمي ، وسوف يقوم Chisel بتوجيه الأسلاك بنفسه بطريقة ما. لم ينجح الأمر. لذلك ، كان لا بد لي من جعل تسخير الأسلاك MemIfBundle
الذي MemIfBundle
جميع أنحاء التسلسل الهرمي. لماذا لا تتمسك به من BlackBox
، ولا تتصل به مرة واحدة؟ الحقيقة هي أنه مع BlackBox
يتم تعبئة جميع المنافذ الخارجية في val io = IO(new Bundle { ... })
. إذا تم إنشاء MemIfBundle
بالكامل في متغير واحد في الحزمة ، MemIfBundle
اسم هذا المتغير بادئة لأسماء جميع المنافذ ، ولن تتزامن الأسماء مع واجهة الكتلة. ربما ، يمكن القيام به بطريقة أو بأخرى بشكل أكثر ملاءمة ، ولكن الآن دعنا نترك الأمر بهذه الطريقة.
علاوة على ذلك ، من خلال القياس مع أجهزة TileLink الأخرى (التي تعيش بشكل أساسي في rocket-chip/src/main/scala/tilelink
) ، وخاصة BootROM
، سنقوم بوصف واجهة جهاز التحكم في الذاكرة الخاص بنا:
class AltmemphyDDR2RAM(implicit p: Parameters) extends LazyModule { val MemoryPortParams(MasterPortParams(base, size, beatBytes, _, _, executable), 1) = p(ExtMem).get val node = TLManagerNode(Seq(TLManagerPortParameters( Seq(TLManagerParameters( address = AddressSet.misaligned(base, size), resources = new SimpleDevice("ram", Seq("sifive,altmemphy0")).reg("mem"), regionType = RegionType.UNCACHED, executable = executable, supportsGet = TransferSizes(1, 16), supportsPutFull = TransferSizes(1, 16), fifoId = Some(0) )), beatBytes = 16 ))) override lazy val module = new AltmemphyDDR2RAMImp(this) } class AltmemphyDDR2RAMImp(_outer: AltmemphyDDR2RAM)(implicit p: Parameters) extends LazyModuleImp(_outer) { val (in, edge) = _outer.node.in(0) val ddr2 = Module(new ddr2_64bit) val mem_if = IO(new MemIfBundle)
ExtMem
مفتاح ExtMem
القياسي ExtMem
نقوم باستخراج معلمات الذاكرة الخارجية من ExtMem
SoC (يسمح لك بناء الجملة الغريب هذا بالقول "أعرف أنهم سيعودون بمثيل لفئة الحالة MemoryPortParameters
(هذا مضمون بنوع المفتاح في مرحلة تجميع رمز Scala ، حسب الحالة أننا لن نقع في وقت التشغيل عن طريق أخذ محتويات من Option[MemoryPortParams]
مساوٍ لـ None
، ولكن بعد ذلك لم يكن هناك شيء لإنشاء وحدة تحكم ذاكرة في System.scala
...) ، لذلك ، لست بحاجة إلى فئة الحالة ، وهناك حاجة إلى بعض حقولها "). بعد ذلك ، نقوم بإنشاء منفذ مدير جهاز TileLink (يضمن بروتوكول TileLink تفاعل كل شيء تقريبًا المتعلق بالذاكرة: وحدة تحكم DDR وغيرها من الأجهزة المعينة في الذاكرة ، وذاكرة التخزين المؤقت للمعالج ، وربما شيء آخر ، يمكن أن يكون لكل جهاز عدة منافذ ، كل الجهاز يمكن أن يكون كل من المدير والعميل). beatBytes
، كما أفهمها ، يحدد حجم معاملة واحدة ، ولدينا 16 بايت يتم تبادلها مع وحدة التحكم. HasAltmemphyDDR2
و HasAltmemphyDDR2Imp
في الأماكن الصحيحة في System.scala
، اكتب التكوين
class BigZeowaaConfig extends Config ( new WithNBreakpoints(2) ++ new WithNExtTopInterrupts(0) ++ new WithExtMemSize(1l << 30) ++ new WithNMemoryChannels(1) ++ new WithCacheBlockBytes(16) ++ new WithNBigCores(1) ++ new WithJtagDTM ++ new BaseConfig )
بعد أن صنعت "رسمًا AltmemphyDDR2RAMImp
" في AltmemphyDDR2RAMImp
، قمت بتجميع التصميم (شيء فقط عند 30 ميغاهرتز تقريبًا ، من الجيد أن أسجل من 25 AltmemphyDDR2RAMImp
، AltmemphyDDR2RAMImp
أصابعي على وحدات الذاكرة ورقاقة FPGA ، قمت بتحميلها على السبورة. ثم رأيت ما هي الواجهة البديهية الحقيقية : هذا عندما تقوم بإعطاء أمر في gdb للكتابة إلى الذاكرة ، وعن طريق معالج متجمد و كتل المخبوزة شعور الأصابع بحرارة قوية ، تحتاج إلى الضغط على زر إعادة الضبط على متن الطائرة بسرعة وإصلاح وحدة التحكم.
اقرأ الوثائق لوحدة التحكم DDR2
على ما يبدو ، لقد حان الوقت لقراءة الوثائق على وحدة التحكم خارج قائمة المنافذ. لذا ، ماذا لدينا هنا؟ .. عفوًا ، اتضح أنه لا ينبغي ضبط I / local_
مع بادئة local_
بشكل متزامن ليس مع pll_ref_clk
، الذي يبلغ 25 pll_ref_clk
، ولكن إما مع phy_clk
الذي phy_clk
نصف تردد الذاكرة لوحدة التحكم بنصف المعدل ، أو ، في حالتنا ، aux_half_rate_clk
(ربما بعد كل aux_full_rate_clk
؟) ، والذي aux_full_rate_clk
تردد الذاكرة الكامل ، وهو لمدة aux_full_rate_clk
.
لذلك ، من الضروري عبور حدود مجالات التردد. وفقًا للذاكرة القديمة ، قررت استخدام المزالج ، أو بالأحرى سلسلة منها:
+-+ +-+ +-+ +-+ --| |--| |--| |--| |---> +-+ +-+ +-+ +-+ | | | | ---+ | | | inclk | | | | | | --------+----+ | outclk | | ------------------+ output enable
لكن ، بعد أن تعاملت مع الساعة ، توصلت إلى استنتاج مفاده أنه لا يمكنني التعامل مع طابور انتظار على المزالج "العددية" (في مجال التردد العالي والعكس بالعكس) ، وسيكون لكل منهما إشارات مضادة للاتجاه ( ready
valid
) ، ومع ذلك ، تأكد من أن بعض beatik لن يتخلف وراء فوز أو اثنين على طول الطريق. بعد فترة من الوقت ، أدركت أن وصف التزامن ready
- valid
دون إشارة ساعة مشتركة هو أيضًا مهمة تشبه إنشاء هياكل البيانات غير المحظورة ، بمعنى أنك بحاجة إلى التفكير وإثبات الكثير رسميًا ، من السهل ارتكاب الأخطاء ، ومن الصعب ملاحظة ذلك ، والأهم من ذلك كله تم تنفيذه أمامنا: لدى Intel مجموعة dcfifo
، وهي قائمة انتظار الطول والعرض dcfifo
للتكوين ، والتي تتم قراءتها وكتابتها من مجالات تردد مختلفة. نتيجةً لذلك ، انتهزت الفرصة التجريبية لإزميل جديد ، أي الصناديق السوداء ذات المعلمات:
class FIFO (val width: Int, lglength: Int) extends BlackBox(Map( "intended_device_family" -> StringParam("Cyclone IV E"), "lpm_showahead" -> StringParam("OFF"), "lpm_type" -> StringParam("dcfifo"), "lpm_widthu" -> IntParam(lglength), "overflow_checking" -> StringParam("ON"), "rdsync_delaypipe" -> IntParam(5), "underflow_checking" -> StringParam("ON"), "use_eab" -> StringParam("ON"), "wrsync_delaypipe" -> IntParam(5), "lpm_width" -> IntParam(width), "lpm_numwords" -> IntParam(1 << lglength) )) { override val io = IO(new Bundle { val data = Input(UInt(width.W)) val rdclk = Input(Clock()) val rdreq = Input(Bool()) val wrclk = Input(Clock()) val wrreq = Input(Bool()) val q = Output(UInt(width.W)) val rdempty = Output(Bool()) val wrfull = Output(Bool()) }) override def desiredName: String = "dcfifo" }
وقد كتب مجهرًا بسيطًا بسيطًا لأنواع البيانات التعسفية:
object FIFO { def apply[T <: Data]( lglength: Int, output: T, outclk: Clock, input: T, inclk: Clock ): FIFO = { val res = Module(new FIFO(width = output.widthOption.get, lglength = lglength)) require(input.getWidth == res.width) output := res.io.q.asTypeOf(output) res.io.rdclk := outclk res.io.data := input.asUInt() res.io.wrclk := inclk res } }
التصحيح
بعد ذلك ، تحولت الشفرة إلى نقل الرسائل بين المجالات من خلال tl_req
ddr_req
بالفعل: tl_req
/ ddr_req
و ddr_resp
/ tl_resp
(يتم tl_
البادئة ذات tl_ مع TileLink ، حقيقة أن ddr_
هي مع وحدة تحكم الذاكرة). المشكلة هي أن كل شيء كان في طريق مسدود على أي حال ، وأحيانا كان الجو حارا جدا. وإذا كان سبب ارتفاع درجة الحرارة هو الإعداد المتزامن لـ local_read_req
و local_write_req
، فليس من السهل القتال مع الجمود. كان الكود في نفس الوقت شيء من هذا القبيل
class AltmemphyDDR2RAMImp(_outer: AltmemphyDDR2RAM)(implicit p: Parameters) extends LazyModuleImp(_outer) { val addrSize = log2Ceil(_outer.size / 16) val (in, edge) = _outer.node.in(0) val ddr2 = Module(new ddr2_64bit) require(ddr2.io.local_address.getWidth == addrSize) val tl_clock = clock val ddr_clock = ddr2.io.aux_full_rate_clk val mem_if = IO(new MemIfBundle) class DdrRequest extends Bundle { val size = UInt(in.a.bits.size.widthOption.get.W) val source = UInt(in.a.bits.source.widthOption.get.W) val address = UInt(addrSize.W) val be = UInt(16.W) val wdata = UInt(128.W) val is_reading = Bool() } val tl_req = Wire(new DdrRequest) val ddr_req = Wire(new DdrRequest) val fifo_req = FIFO(2, ddr_req, ddr_clock, tl_req, clock) class DdrResponce extends Bundle { val is_reading = Bool() val size = UInt(in.d.bits.size.widthOption.get.W) val source = UInt(in.d.bits.source.widthOption.get.W) val rdata = UInt(128.W) } val tl_resp = Wire(new DdrResponce) val ddr_resp = Wire(new DdrResponce) val fifo_resp = FIFO(2, tl_resp, clock, ddr_resp, ddr_clock)
لتوطين المشكلة ، قررت تعليق جميع التعليمات البرمجية الموجودة داخل withClock(ddr_clock)
(لا يبدو ذلك بصريًا مثل إنشاء دفق) واستبداله withClock(ddr_clock)
يعمل بشكل مؤكد:
withClock (ddr_clock) { ddr_resp.rdata := 0.U ddr_resp.is_reading := ddr_req.is_reading ddr_resp.size := ddr_req.size ddr_resp.source := ddr_req.source val will_read = Wire(!fifo_req.io.rdempty && !fifo_resp.io.wrfull) fifo_req.io.rdreq := will_read fifo_resp.io.wrreq := RegNext(will_read) }
كما أدركت لاحقًا ، لم يعمل هذا كعب الروتين أيضًا لأن بنية Wire(...)
، التي أضفتها "من أجل الموثوقية" لإظهار أنها سلك مسمى ، استخدمت في الواقع الوسيطة فقط كنموذج أولي لإنشاء نوع المعنى ، لكنه لم يربطه بحجة التعبير . أيضًا ، عندما حاولت قراءة ما تم إنشاؤه ، أدركت أنه في وضع المحاكاة يوجد مجموعة واسعة من التأكيدات المتعلقة بعدم الامتثال لبروتوكول TileLink. من المحتمل أن يكونوا في متناول يدي فيما بعد ، لكن حتى الآن لم تكن هناك أي محاولة لتشغيل المحاكاة - لماذا تبدأ؟ ربما لا تعرف Verilator عن Alter's IP Cores ، ومن المحتمل أن يرفض ModelSim Starter Edition محاكاة مثل هذا المشروع الضخم ، لكنني أقسمت أيضًا بعدم وجود نموذج وحدة تحكم للمحاكاة. ولإنشاءها ، ربما تحتاج إلى التبديل أولاً إلى الإصدار الجديد من وحدة التحكم (لأنه تم تكوين القديم في Quartus القديمة).
في الواقع ، تم أخذ كتل التعليمات البرمجية من إصدار شبه عمل ، وليس تلك التي تم تصحيحها بشكل نشط قبل ساعات قليلة. لكنك أفضل ؛) بالمناسبة ، يمكنك إعادة تجميع التصميم باستمرار بشكل أسرع إذا تم استبدال الإعداد WithNBigCores(1)
بـ WithNSmallCores(1)
- من وجهة نظر الوظيفة الأساسية WithNSmallCores(1)
تحكم الذاكرة ، يبدو أنه لا يوجد فرق. وخدعة صغيرة: لكي لا أقود نفس الأوامر إلى gdb في كل مرة (على الأقل ليس لدي تاريخ من الأوامر بين الجلسات هناك) ، يمكنك ببساطة كتابة شيء مثل هذا في سطر الأوامر
../../rocket-tools/bin/riscv32-unknown-elf-gdb -q -ex "target remote :3333" -ex "x/x 0x80000000" ../../rocket-tools/bin/riscv32-unknown-elf-gdb -q -ex "target remote :3333" -ex "set variable *0x80000000=0x1234"
وتشغيل حسب الحاجة باستخدام الوسائل العادية للقذيفة.
يؤدي
نتيجة لذلك ، تم الحصول على الكود التالي للعمل مع وحدة التحكم:
withClock(ddr_clock) { val rreq = RegInit(false.B)
سنستمر هنا في تغيير معيار الإكمال قليلاً: لقد رأيت بالفعل كيف ، بدون أي عمل مع الذاكرة ، تكون البيانات المسجلة كما لو كانت للقراءة ، لأنها ذاكرة تخزين مؤقت. لذلك ، نقوم بتجميع قطعة بسيطة من التعليمات البرمجية:
#include <stdint.h> static volatile uint8_t *x = (uint8_t *)0x80000000u; void entry() { for (int i = 0; i < 1<<24; ++i) { x[i] = i; } }
../../rocket-tools/bin/riscv64-unknown-elf-gcc test.c -S -O1
نتيجة لذلك ، نحصل على الجزء التالي من قائمة المجمّع ، مع تهيئة 16 ميجابايت من الذاكرة الأولى:
li a5,1 slli a5,a5,31 li a3,129 slli a3,a3,24 .L2: andi a4,a5,0xff sb a4,0(a5) addi a5,a5,1 bne a5,a3,.L2
bootrom/xip/leds.S
في بداية bootrom/xip/leds.S
من غير المحتمل الآن أن يكون كل شيء قادرًا على الاحتفاظ بذاكرة تخزين مؤقت واحدة فقط. يبقى تشغيل Makefile ، وإعادة بناء المشروع في Quartus ، وملء اللوحة ، وربط OpenOCD + GDB و ... من المفترض ، هتافات ، النصر:
$ ../../rocket-tools/bin/riscv32-unknown-elf-gdb -q -ex "target remote :3333" Remote debugging using :3333 warning: No executable has been specified and target does not support determining executable automatically. Try using the "file" command. 0x0000000000010014 in ?? () (gdb) x/x 0x80000000 0x80000000: 0x03020100 (gdb) x/x 0x80000100 0x80000100: 0x03020100 (gdb) x/x 0x80000111 0x80000111: 0x14131211 (gdb) x/x 0x80010110 0x80010110: 0x13121110 (gdb) x/x 0x80010120 0x80010120: 0x23222120
هل هو كذلك ، سوف نكتشف ذلك في السلسلة التالية (لا أستطيع أن أقول أيضًا عن الأداء والاستقرار وما إلى ذلك).
الرمز: AltmemphyDDR2RAM.scala .