سؤال

على نظام مضمن ، لدينا إعداد يسمح لنا بقراءة البيانات التعسفية عبر واجهة سطر الأوامر لأغراض التشخيص. بالنسبة لمعظم البيانات ، هذا يعمل بشكل جيد ، نستخدمه memcpy() لنسخ البيانات على العنوان المطلوب وإرسالها مرة أخرى عبر اتصال تسلسلي.

ومع ذلك ، بالنسبة لسجلات الأجهزة 16 بت ، memcpy() يسبب بعض المشاكل. إذا حاولت الوصول إلى سجل أجهزة 16 بت باستخدام اثنين من الوصول إلى 8 بت ، فإن البايت عالي الترتيب لا يقرأ بشكل صحيح.

وقد أي شخص واجه هذه المسألة؟ أنا رجل "رفيع المستوى" (C#/Java/Python/Ruby) يقترب من الأجهزة وهذا هو منطقة غريبة.

ما هي أفضل طريقة للتعامل مع هذا؟ أرى بعض المعلومات ، على وجه التحديد ، مربكة إلى حد ما [بالنسبة لي هنا. مؤلف هذا المنشور بالضبط نفس المشكلة التي أقوم بها ولكني أكره تنفيذ حل دون أن أفهم تمامًا ما أفعله.

أي ضوء يمكنك إلقاءه على هذه المسألة موضع تقدير كبير. شكرًا!

هل كانت مفيدة؟

المحلول

بالإضافة إلى ماذا قال إدي, ، عادة ما تحتاج إلى استخدام مؤشر متقلبة لقراءة سجل الأجهزة (على افتراض سجل تم تعيين الذاكرة ، وهذا ليس هو الحال بالنسبة لجميع الأنظمة ، ولكن يبدو أنه صحيح بالنسبة لك). شيء مثل:

// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)

uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd;  // whatever the reg address is

uint16_t val = *pReg;  // read the 16-bit wide register

فيما يلي سلسلة من المقالات التي كتبها Dan Saks التي يجب أن تمنحك كل ما تحتاج إلى معرفته لتكون قادرًا على استخدام سجلات الذاكرة المعينة بفعالية في C/C ++:

نصائح أخرى

كل سجل في هذا الجهاز يتعرض كصفيف ثنائي البايت ، يتم محاذاة العنصر الأول على حدود ثنائية البايت (عنوانه متساوي). يقوم Memcpy () بتشغيل دورة ونسخ بايت واحد في كل تكرار ، بحيث ينسخ من هذه السجلات بهذه الطريقة (جميع الحلقات غير المنطوقة ، تشار واحد بايت):

*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd

ومع ذلك ، يعمل السطر الثاني بشكل غير صحيح لبعض الأسباب المحددة للأجهزة. إذا قمت بنسخ اثنين من البايت في وقت واحد بدلاً من واحد في وقت واحد ، فسيتم ذلك بدلاً من ذلك بهذه الطريقة (int القصير هو بايتان):

*((short int*)target) = *((short*)register;// evenly aligned

هنا تقوم بنسخ اثنين من البايتات في عملية واحدة ويتم محاذاة البايت الأول بالتساوي. نظرًا لعدم وجود نسخ منفصل من عنوان محاذاة بشكل غريب ، فإنه يعمل.

يتحقق memcpy المعدل مما إذا كانت العناوين محاذاة ونسخ ببايتات السحب إذا كانت كذلك.

إذا كنت بحاجة إلى الوصول إلى سجلات الأجهزة ذات الحجم المحدد ، فلديك خياران:

  • فهم كيف يقوم برنامج التحويل البرمجي C بإنشاء رمز حتى تتمكن من استخدام نوع عدد صحيح مناسب للوصول إلى الذاكرة ، أو
  • قم بتضمين بعض التجميع للقيام بالوصول باستخدام البايت الصحيح أو حجم الكلمة.

يمكن أن يكون لسجلات القراءة للأجهزة تأثيرات جانبية ، اعتمادًا على السجل ووظائفه ، بالطبع ، لذلك من المهم الوصول إلى سجلات الأجهزة مع الوصول المناسب بالحجم المناسب حتى تتمكن من قراءة السجل بأكمله دفعة واحدة.

عادة ما يكون ذلك كافيًا لاستخدام نوع عدد صحيح بنفس حجم السجل الخاص بك. على عظم المترجمون ، قصير 16 بت.

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}

أعتقد أن كل التفاصيل موجودة في هذا الموضوع الذي نشرته ، لذا سأحاول تقسيمه قليلاً ؛

خاصة؛

If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

وبالتالي فإن المشكلة هنا هي أن سجلات الأجهزة الإبلاغ عن القيمة الصحيحة فقط إذا تمت قراءة قيمها في قراءة واحدة 16 بت. هذا سيكون مكافئا للقيام ؛

uint16 value = *(regAddress);

يقرأ هذا من العنوان إلى سجل القيمة باستخدام قراءة واحدة 16 بايت. من ناحية أخرى ، لديك memcpy تقوم بنسخ البيانات ببايت واحد في وقت واحد. شيء مثل؛

while (n--)
{
  *(uint8*)pDest++ = *(uint8*)pSource++;
}

لذلك يؤدي هذا إلى قراءة السجلات 8 بتات (بايت 1) في وقت واحد ، مما يؤدي إلى أن تكون القيم غير صالحة.

يتمثل الحل المنشور في هذا الموضوع في استخدام إصدار من memcpy يقوم بنسخ البيانات باستخدام قراءات 16 بت أينما كانت المصدر والوجهة محاذاة A6 بت.

ماذا تريد ان تعرف؟ لقد وجدت بالفعل منشورًا منفصلًا يشرح ذلك. يبدو أن وثائق وحدة المعالجة المركزية يستوجب يتم الوصول إلى سجلات الأجهزة التي تبلغ 16 بت من خلال قراءة وتكتب 16 بت ، لكن تطبيق Memcpy الخاص بك يستخدم قراءات/كتابة 8 بت. لذلك لا يعملون معًا.

الحل هو ببساطة عدم استخدام memcpy للوصول إلى هذا السجل. بدلاً من ذلك ، اكتب روتينك الخاص الذي يقوم بنسخ القيم 16 بت.

لست متأكدًا بالضبط ما هو السؤال - أعتقد أن المنشور لديه الحل الصحيح. كما ذكرت ، فإن المشكلة هي أن روتين memcpy () القياسي يقرأ بايت في وقت واحد ، والذي لا يعمل بشكل صحيح لسجلات الأجهزة المعينة للذاكرة. هذا هو قيود على المعالج - ببساطة لا توجد طريقة للحصول على قيمة صالحة للقراءة بايت في الوقت المناسب.

الحل المقترح هو كتابة memcpy () الذي يعمل فقط على عناوين محاذاة الكلمات ، ويقرأ كلمات 16 بت في وقت واحد. هذا واضح إلى حد ما - الرابط يعطي كل من AC ونسخة التجميع. GOTCHA الوحيد هو التأكد من قيامك دائمًا بالنسخ 16 بت من العنوان المحاذاة بشكل صحيح. يمكنك القيام بذلك بطريقتين: إما استخدام أوامر Linker أو pragmas للتأكد من محاذاة الأمور ، أو إضافة حالة خاصة للبايت الإضافي في مقدمة المخزن المؤقت غير المحدد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top